Here it is: the ggplot advent calendaR! The “gg” in ggplot refers to the grammar of graphics. For the next 25 days, we will go through an introduction to the grammar of graphics, make a lot of visualizations (some good, some bad), and learn some of the basic functions and features of the ggplot2 package. NOTE I am no expert in ggplot. I literally learned while creating this tutorial, and that was a big motivation in doing this. If there are errors or smoother ways to do the same thing, please let me know! R is about constant learning and improving, ggplot is no different.

DAY 1

On the first day of Christmas… we’re jumping into the tidyverse!

ggplot is part of the tidyverse, a group of packages that also includes dplyr, readr, and other very helpful packages that you should have! You can install and load ggplot separately, but… why? (:

The package we’re using is actually called ggplot2. Super-duper fun fact: “ggplot2 is called ggplot2 because once upon a time there was just a library ggplot. However, the developer noticed that it used an inefficient set of functions. In order for not to break the API, the authors introduced a successor package ggplot2. However, the central function in this package is still called ggplot(), not ggplot2()!” (wasn’t that fun? Source: Freeman & Ross, 2019).

Install and load tidyverse:

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.4.1 
✔ readr   2.1.2      ✔ forcats 0.5.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ ggplot2::annotate() masks ctmm::annotate()
✖ dplyr::filter()     masks stats::filter()
✖ dplyr::lag()        masks stats::lag()

Let’s also load our data using another tidyverse package, readr, and then view the data. We will be working with two datasets throughout this tutorial:

Using readr and read_csv(), instead of read.csv() from base R, loads our data in as tibbles. Why use tibbles over traditional dataframes? Three reasons: (1) the input types aren’t automatically changed when you read in the data, (2) you can keep lists as columns, and (3) you can use non-standard variable names (e.g., starting with a number, as in “1st_place”). Thank you to my friend Pat for this explanation!

DAY 2

On the second day of Christmas…

…we learned the language of ggplot aka the Grammar of Graphics! ggplot is different from base R graphics. How? Base R graphics work on the individual vectors, ggplot works on dataframes (Source: Prabhakaran, 2017). ggplot works by adding layer upon layer to create your visualization. In base R graphics, you put all the information in one code and it spits out a graphic. In ggplot, you build your grahpic with layers.

What are some of the different components of a graphic (Source: Freeman & Ross, 2019)? - data - geometric objects (geoms) - aesthetics - statistical transformations - position adjustments - scale - coordinate system - facets - themes

The first layer of ggplot is always…ggplot. If you run the code below, you will get a blank graphic with a grey background. The grey background is the ggplot default. In base R, you can’t run just function, e.g., boxplot(), without a vector in the brackets.

Now let’s add another layer to ggplot. We’ll specify the dataset and what we want our x and y axes to be.

When you run this code, you should see that we now have axes and labels on top of our grey background. Note, you can drop the x= and y= and the code will run the same (don’t take my word for it, try it yourself!).

DAY 3

On the third day of Christmas…

…we added data to our graphic and explored geom layers!

Geoms, or geometric objects, are graphical representations of the data. There are many many types of geoms (here’s a long list of examples: https://ggplot2.tidyverse.org/reference/#geoms). Let’s try a few.

Geom layers start with “geom_” and are followed by the type of geometric object, e.g., “geom_point” or “geom_line”. Because we’re adding a new layer onto our graphic, we use a + after our first line of code. To keep things tidy and easy to read, I usually start my new layer on a new line.

Since we are working with categorical data (types of trees), the points all fall on three lines. Let’s change our x and y axes before moving on to a different type of geom.

Now we’re comparing two continuous variables (xmas magic and tree height) so the points are scattered across our graphic.

Let’s look at some different geom types. We’ll go back to type vs. height for this one.

When creating graphics, always consider the type of data you’re working with (e.g., continuous vs. discrete). The type of data you’re working with should determine the type of geom you choose. Some geoms won’t run properly if the type of data you’re inputting doesn’t work with those data types.

One more example of a geom layer using the trees data.

DAY 4

On the fourth day of Christmas…

…we introduced pipes!

This is a bit of a segue from ggplot and you certainly don’t need to use pipes to create beautiful ggplot graphics BUT it might make your data look more tidy.

The pipe operator (%>%) is included in the tidyverse, so if you loaded the tidyverse, then you’ve got access to %>%. From R for Data Science, “Pipes are a powerful tool for clearly expressing a sequence of multiple operations…The point of the pipe is to help you write code in a way that is easier to read and understand.” In words, %>% means “and then”. By using a pipe, you’re telling R to run the first line of code AND THEN run the next line of code.

Why do I bring this up now? Previously, we ran this code to produce a boxplot showing tree heights:

Often, in online examples and tutorials, you will see ggplot codes written with the dataframe listed first, then a pipe leading into the ggplot code. If you run the code below, you’ll see that we can produce the exact same graphic:

Another tip from my friend Pat, “The most useful application of the pipe is to plug the result of one function into another without creating intermediate data frames” Here’s an example of what this might look like:

my_data %>% function1(arguments, etc) %>% function2(arguments, etc)

DAY 5

On the fifth day of Christmas…

…we started working with the aesthetics of our graphic!

Aesthetic mapping describes the visual properties of the graphic. We can change the aesthetics of ggplot() or each layer we add to it. We use aes() and then customize our graphic to appear how we’d like it. On Day 3, we created some pretty boring graphs: white boxes or violins, black dots, and grey backgrounds. While this is fine for exploring data, it’s perhaps not how we want our finished product to look!

Today, we’re going to work on changing the colours of our data. Let’s start with the scatterplot, i.e., the geom_point() graphic.

To change the aesthetics of the geom_point() dots, we add “aes()” within the parentheses of geom_point(). You may have noticed that we already used “aes()” when we told ggplot what we wanted our X and Y axes to be. Now we will also tell ggplot to assign different colours to our points by tree type. In this case, we are not choosing the colours.

Now instead of changing the aesthetics of the ggplot() layer, let’s instead change the aesthetics of the geom_point() layer.

Looks the same right? So what’s the difference? Right now, nothing appears different, but later it may impact how your graphic looks. When you change the aesthetics of the ggplot() layer, those aesthetics will be applied as the default to all of your subsequent layers. If you change the aesthetics of an individual layer, it will only be applied to that layer and it will override the default.

What if we want to use all the same colour for our points? Find out tomorrow!

DAY 6

On the sixth day of Christmas…

…we continued with aesthetic mapping and colours!

So you want all your points to be one colour. Sounds easy-peasy right? Not quite.

Here’s why it’s a bit confusing. I had to do a bit of digging to understand exactly why this is…

When we want to map a variable of our data (e.g., telling ggplot we want to colour by tree type), we put aes() inside the geom_point(). If we want to apply a constant colour (constant value) to our points (e.g., telling ggplot we want all our points to be blue), we put aes() OUTSIDE geom_point). Try both the codes below to see what happens.

Note: in this case you could also leave out the aes() within geom_point() and it would run the same.

For more of an explanation on why aes() works this way, you can check out this helpful thread on stackoverflow that helped me: https://stackoverflow.com/questions/41863049/when-does-the-argument-go-inside-or-outside-aes. Here’s another really fantastic resource: https://drive.google.com/file/d/1Dvul1p6TYH6gWJzZRwpE0YX1dO0hDF-b/view.

DAY 7

On the seventh day of Christmas…

…we changed the colours of boxplots!

Try running the same code from yesterday but on a geom_boxplot() graphic.

Hmm… okay. But what if we wanted the inside of the boxes to be coloured, not the outline?

Instead of “colour =” we use “fill =” instead.

This works for points too. The default for geom_point() are solid points, but you can change these to points with different outline and fill colours.

We can also change the colours of our boxplots by tree type, as we did with the points on Day 5. Remember from yesterday that because we want to change the colours by a variable (i.e., multiple colours determind by the levels of the variable) rather than change using a constant value (i.e., single colour), we put this INSIDE aes().

Now that we’re playing around with colours. It’s time I introduce you to a good colour resource: http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf. There are many great online resources about colours, but I like this one made by Dr. Ying Wei.

DAY 8

On the eight day of Christmas…

…we finally took a break from talking about colours!

Colour is a big one, but let’s work with some other changes to the aesthetics of our graphic. Let’s bring back the geom_point() graphic. I’m using the theme of Christmas, but of course, December has many, many holidays. Hanukkah starts on Dec. 17 this year, so let’s make a Hanukkah-inspired graphic and look at different ways to change the aesthetics of our graphs.

Here we’ve specified the shape, the colour, the size, and the stroke (line thickness) of the points. There are many changes we can make to aesthetics, these are just a few examples. Let’s take a look at a line graph and how we can customize that.

We haven’t made a line graph yet, so let’s create a simple one first, before we customize it.

Here we’ve specified that we want each line to represent a different tree type by using “group=”. This goes inside aes() because it is being applied to the data (i.e., it can’t be done without this specific dataset)

Now let’s spruce it up. (“spruce” it up… because they’re trees… hahahaha. A little Christmas cheer for you).

What if we want to show the points AND the lines? Tomorrow we will add another layer to our graphic.

DAY 9

On the ninth day of Christmas…

… we added another geom_ layer to our graphic.

Here’s our geom_lin() graphic from yesterday:

Now let’s add points to it as well. It’s as simple as + geom_point()!

And if you want to change the aesthetics of those points, where do you do it? Within geom_point()!

They kind of look like Christmas lights :)

DAY 10

On the tenth day of Christmas…

…we edited the text of our graphics.

Editing the look of your text and fonts in ggplot is easy, with lots of options to make it look exactly how you want it. Here’s our graphic from yesterday.

What kind of things might we want to change? Maybe we want a title and more informative and cleaner-looking axis labels. To do this, we need to add new layer, labs() for labels. You can also use xlab(), ylab(), and ggtitle() to add them individually.

What if we want to make more aesthetic changes to our fonts and labels? We will need to use themes!

DAY 11

On the eleventh day of Christmas…

…we introduced themes!

We can use themes to make finer adjustments to non-data parts of our graphic. While labs() is fine for adding labels and a title, themes allow us to choose the size, font, colour, position, etc. of that text. Today I’m going to introduce you to some complete themes.

Let’s go back to our nice, clean-looking boxplots:

While the grey backgronud is the default in ggplot, it’s certainly not a requirement. That’s often one of the first things I change!

Theme_bw() is a complete theme, meaning that it’s a theme that can be applied as a layer that changes the look of your overall plot. Theme_gre() is the default. You can also try other complete themes, such as theme_dark(), theme_light(), theme_classic(), theme_void() and more.

Personally, I like this one best!

DAY 12

On the twelfth day of Christmas…

… we worked with theme() and more control with non-data parts of our graphics!

Here, I’m keeping theme_classic() and building on top of that.

In this case, I wanted to show you how you can keep the grid lines, while changing to theme_classic(). We’ve also changed the style and size of the title, moved the legend and changed the colours, changed the colour of the y axis label, and changed the size of the x axis tick labels. This is by no means a pretty graphic, but hopefully it gives you an idea of different ways we can change features of our graphic. There are many more changes you could make and lots of great online tutorials that cover this in more detail.

DAY 13

On the thirteenth day of Christmas…

… we introduced scales!

To do this, we need to introduce scales. We’ll be working with scales for a few days - get excited! Scales allow us to override defaults. Similar to themes, scales allow us more control over what our graphic looks like, but scales focus on changing the look of the data.

Let’s start with position scales. The most commonly used are scale_x_continuous() and scale_y_continuous(). Since we are working with categorical data right now (tree types), we could swap out scale_x_continuous() for scale_x_discrete(). Using these, we can set the limits of our scales. We don’t need to change the limits of our discrete (x) axis, but let’s change the limits of our y axis.

Here’s our boxplot graphic, but I’ve changed our y axis to Christmas magic instead of tree height:

trees %>%
ggplot(aes(x=type, y=xmas.magic))+
  geom_boxplot(aes(fill=type), colour="black")+
  labs(title="Christmas Trees", x=NULL, y="Christmas magic")+
  theme_classic()

Now let’s try changing the y-axis limits:

Note: you can also use lims(x=c(#,#), y=c(#,#)) (replacing the #s with your desired limits). This is simpler and faster, but we will stick with scale_y_continuous() because we will make additional adjustments below.

When we increased our y-axis limits, it changes the labels of our y-axis ticks to include 0.5s. Maybe we’d rather have whole numbers or maybe just fewer numbers altogether. We can specify this using the same scale_y_continuous() but adding “breaks=”.

If we don’t want any breaks we can specify by using “breaks=NULL”.

Finally, we can modify the axis tick labels. Let’s change the names of our x-axis tick labels.

There’s much much more that can be done with position scales. I suggest taking a look at Ch. 10 of this book by Hadley Wickham, Danielle Navarro, and Thomas Lin Pedersen, which I relied on heavily for this section. https://ggplot2-book.org/scale-position.html#scale-position

DAY 14

On the fourteenth day of Christmas…

… we worked with colour scales!

We’ve been seeing the same three colours over and over: red, green, blue. But what if we want to specify the colours when we choose colour or fill by type? This is where colour scales come in.

We can choose a different colour palettes by installing colour palette packages and loading them.

Here is a good resource, RColorBrewer.

install.packages("RColorBrewer")
library(RColorBrewer)

Let’s choose a palette from RColorBrewer. You can find the names and palettes here: https://r-graph-gallery.com/38-rcolorbrewers-palettes.html or you can run this code to display them in R:

Here we’ll create our boxplots again, so we will want to change the “fill” rather than the “colour”, therefore we use scale_fill_brewer(). We’re going to try the Dark2 palette:

What if you like the palette but don’t like how ggplot applied the colours? Or maybe you can’t find the perfect palette and want to create your own? We can manually assign colours too… but we’ll wait until tomorrow for that one!

DAY 15

On the fifteenth day of Christmas…

…we learned how to assign colours manually!

For this we use scale_color_manual() and/or scale_fill_manual(). For the boxplots, we will use scale_fill_manual().

The colours will be assigned in the order we gave them, so you can also repeat a colour (e.g., green, red, green) and it will be assigned in that order. Instead of colour names, you can also use the color codes (e.g., #E69F00).

We’ll be using these colours again and again, so why don’t we save them as a vector?

xmas <-c("darkgreen", "firebrick2", "mediumseagreen")

You can also specific the colours that will be assigned to each level of your variable:

Christmas colours aren’t necessarily colour-blind friendly. There are lots of fantastic resources when considering colour-blind friendly palettes for your graphics. Here’s one: https://colorbrewer2.org/#type=sequential&scheme=YlGnBu&n=9

DAY 16

On the sixteenth day of Christmas…

…we worked with a different type of plot and added multiple geoms to one plot!

We haven’t even touched the sleigh dataset! Let’s create a graphic with that so we can work with a different type of plot.

Here we’ve told ggplot to colour each sleigh type a different colour (some are nearly impossible to differentiate but we won’t worry about that today).

Maybe we want to add a trendline to our plot. We can do this by adding a geom_smooth() layer:

And maybe we want to customize that line. The default is a blue line with grey background. We can change that to a black line using “colour =” and we can change the transparency of the error using “alpha =”. We can also change the method of how the line is calculated using “method =”

Alpha can also be helpful when you have overlapping points.

More detailed and additional information on colour scales can be found here: https://ggplot2-book.org/scale-colour.html

DAY 17

On the seventeenth day of Christmas…

…we edited our legend!

Want to move your legend? Change the shape, style, size? Get rid of it completely?

Let’s go back to our boxplots.We can remove the legend using “show.legend = FALSE”. We put this in the geom_boxplot() since the legend is linked to that layer.

Another option is to use guides(). In this case we use fill=“none” but if we were working with colour instead of fill, we would type colour=“none”. This way of removing the legend is handy in instances when you have multiple geoms. We can add one little line of code to remove the legend, instead of typing “show.legend=F” into each geom layer.

We can also change the text of the legend. When we changed the x-axis labels, the legend didn’t change with it. Let’s fix that now. We do this by adding the same “labels = c(…” in scale_fill_manual() as well as scale_x_discrete(). Why? Because scale_fill_manual() refers to the colours of your data, and the legend represents that (they are directly linked). scale_x_discrete() is focused solely on the x-axis.

REFRESHER What if we want to change the position of the legend or the colour of the text?? Remember back to when we talked about themes? If we want to make changes to anything that’s not related to the data (i.e., it could be a plot of anything or one without any data in it), we use THEMES.

DAY 18

On the eighteenth day of ChRistmas…

… we learned about guides!

Yesterday, we made some edits to our legend using scales and themes. Today, we will introduce one more way to exercise more fine control over your graphics: guides() and guide_ functions! Guides, like our legends and axes, help us or our audience interpret our plots. We can use guides() or the guide_ argument _*() functions to make additional changes to our legends and axes. Here’s a great explanation of scales and guides from Ch.15 of Wickham, Navarro and Pederson’s book, which I highly recommend you check out: https://ggplot2-book.org/index.html

“Formally, each scale is a function from a region in data space (the domain of the scale) to a region in aesthetic space (the range of the scale). The axis or legend is the inverse function, known as the guide: it allows you to convert visual properties back to data. You might find it surprising that axes and legends are the same type of thing, but while they look very different they have the same purpose: to allow you to read observations from the plot and map them back to their original values.”

Let’s look at some examples. Let’s try a new graphic with our data, and we’ll use a gradient colour scale to colour our points based on the amount of “Christmas magic” in our trees:

Before we get back to guides, let’s quikcly chat about the gradient scale. There are many, many ways you can edit the colours, but in this case we told ggplot that we wanted to change the colour of our points with a gradient “scale_colour_continuous()” and then we set the high and low colours. We could have also set the middle colour or chosen an existing gradient. Learn more here: https://ggplot2-book.org/scale-colour.html

Back to guides! (colours are just so distracting!!)

We can make additional edits to our legends using “+ guides()” or by specifying the “guide =” argument within our scale layer (scale_colour_continuous(), which corresponds with our legend).

Here we’ve flipped our bar horizontally and increased the size of the legend. No changes have been made to the rest of our graphic. We can achieve the exact same output by adding “guide =” to our scale_colour_continuous() layer.

trees %>%
ggplot(aes(x=needle.drop, y=height))+
  geom_point(aes(colour=xmas.magic), size=2)+
  theme_classic()+
  scale_colour_continuous(low="red", high="mediumseagreen", guide = guide_colourbar(reverse=TRUE, direction = "horizontal", barheight=unit(2, "cm")))

Here are a couple more ways we can use guides to edit our legend. Let’s change up our plot a bit.

There’s a bit of overlap in our points, so let’s adjust the transparency using alpha:

But maybe we don’t want our legend to also have transparent points, so we can use a guide to override this aesthetic change.

Finally, let’s use guides to change the aesthetics of our axes. We will go back to our sleighs dataset for this one.

sleighs %>%
ggplot(aes(x=name, y=deerpower))+
  geom_point()+
  labs(x="Sleighs")+
  theme_classic()

As you can see, the names of the sleighs are impossible to read. Let’s flip the labels at the bottom so that they run vertically instead.

sleighs %>%
ggplot(aes(x=name, y=deerpower))+
  geom_point()+
  theme_classic()+
  labs(x="Sleighs")+
  guides(x=guide_axis(angle=90))

Much better! Hopefully now you have an idea of some of the ways you can edit your guides (legends, axes) using guides() and guide =.

Day 19

On the nineteenth day of Christmas…

… we made position adjustments!

Position adjustments are handy if you have overlapping geoms or data. You can override the default using the position argument in the geom_() functiions.

Instead of boxplots, let’s look at the raw data points using a different type of geom later, geom_jitter().

Position adjustments come in handy with point data like this, more so when we’re working with large datasets that have many points. Let’s adjust the position of our jittered points.

Let’s look at another example. We’ll bring back our scatterplot of sleigh data but I’m going to cut it down a bit to make it a easier to work with. I’ll do this using another tidyverse package, dplyr.

That legend is fine, but let’s get rid of it and instead label each point.Do you remember how to remove the legend?

sleighs.subset %>%
  ggplot(aes(x=deerpower, y=km_per_carrot))+
  geom_point(aes(fill=name), shape=21, size=3, show.legend = F)+
  geom_text(aes(label=name))+
  theme_classic()

Well this might work better, but the labels are all overlapping and difficult to read. This is where position_nudge() comes in handy!

And because our Stealth Sleigh is off the plot, let’s fix that using what we learned on Day 13 about limits.

DAY 20

On the twentieth day of Christmas…

… we did some position adjustments with bar plots!

First, let’s create a barplot since we haven’t done that yet. We’ll base it on our previous sleighs subset.

sleighs.subset %>%
  ggplot(aes(x=bells, y=reins))+
  geom_col()+
  scale_x_discrete(limits=c(4, 6, 8))+
  theme_classic()
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

The sleighs come with 4, 6, or 8 bells. So here we’re displaying the counts of sleighs in each category (# of bells). But maybe we want to see some additional information in our barplot, such as the number of reins on the sleigh. I’ve heard that Santa takes these things super seriously, so this is completely practical and reasonable plot. Note that we have to specify that reins is a categorical variable, not a continuous one, using as.character(). In this case, we can’t have 3.5 reins.

sleighs.subset %>%
  ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
  geom_col()+
  scale_x_discrete(limits=c(4, 6, 8))+
  theme_classic()+
  scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

The default is a stacked barplot (position = “stack”), but there are other ways we could display this using position adjustments. This option shows it as a percent using position = “fill”.

sleighs.subset %>%
  ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
  geom_col(position="fill")+
  scale_x_discrete(limits=c(4, 6, 8))+
  theme_classic()+
  scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

We can also position them side by side using position = “dodge”. Note that the red bar on the left and the green bar on the right are two bars side by side.

sleighs.subset %>%
  ggplot(aes(x=bells, y=reins, fill=as.character(reins)))+
  geom_col(position="dodge")+
  scale_x_discrete(limits=c(4, 6, 8))+
  theme_classic()+
  scale_fill_manual(values=xmas)
Warning: Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

Want a review? Try changing the name of your legend.

DAY 21

On the twenty-first day of Christmas…

…we learned about faceting!

Faceting produces smaller graphs that can be displayed alongside one another. We use facet_wrap() and facet_grid() for this.

Let’s start with facet_wrap(). Remember our line graph that looks a bit like Christmas lights? Let’s use that. Here it is, as a reminder:

xmas
Error: object 'xmas' not found

Now, instead of having all three lines on one plot, let’s create three smaller plots and display them together.

Let’s do the same thing but using facet_grid(). The syntax is a little different, but we’ve produced the exact same set of plots. In our case, “.~type” puts the plots side by side.

trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
 geom_line(linetype="dashed")+
  geom_point(size = 3, shape = 8)+
  facet_grid(.~type)+
  guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

If we want to stack our plots instead, we change up the coding within facet_grid(). In our case, “type~.” stacks the plots.

trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
 geom_line(linetype="dashed")+
  geom_point(size = 3, shape = 8)+
  facet_grid(type~.)+
  guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

Our datasets aren’t really set up for this type of grid, but let’s look at plots of reins by bells to show you how you could set up facet_grid() with multiple plots. A plot area is produced with two levels for reins and three levels for bells.

You can also use “scales =” to adjust the scales of all or each of the plots. Let’s go back to our first set of plots from today:

trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
  geom_line(linetype="dashed")+
  geom_point(size = 3, shape = 8)+
  facet_wrap(~type, ncol=3)+
  guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

To adjust the scales, we add “scales =” to facet_wrap().

trees %>%
ggplot(aes(x=height, y=xmas.magic, group=type))+
  geom_line(linetype="dashed")+
  geom_point(size = 3, shape = 8)+
  facet_wrap(~type, ncol=3, scales = "free_y")+
  guides(colour=FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

By choosing “free_y” we have changed the y scale from fixed to free but the x-axis remained the same. Take a look at how the y-axis scale is now different for each plot. Other options are “free_x”, “fixed”, or “free”.

There are lots more things you can do with facets. Check out Ch. 17 of “ggplot2:Elegant Graphics for Data Analysis” for more: https://ggplot2-book.org/facet.html

Want a review? Try changing the style of the points and lines in these plots. Also, try removing the grey background!

DAY 22

On the twenty-second day of Christmas…

…we annotated our plots!

Sometimes we may want to add text to our plots, not just titles and labels, but annotations on the data or plot area. We can do this using geom_text(). We did this on Day 19 when we talked about position adjustments. Now we’re going to discuss annotations in more detail. Let’s bring back that graphic, but with some added text and labels.

Let’s say that the elf in charge wants to send this to Santa but wants to mark, on the plot, which sleigh is her top choice for Santa. We can do this using the annotate() function. We first need to set up our x & y ranges and our caption text.

yrng <- range(sleighs.subset$km_per_carrot)
xrng <- range(sleighs.subset$deerpower)
caption <- paste(strwrap("Sprinkle's top choice: Winter Express"))

Then we can make our plot:

Or may we want to annotate the points directly to show Santa which ones Sprinkle chose as her top recommendations.

Hopefully it’s clear which point we’re referring to here (“Winter Express”) but in case it’s not, we can also highlight it by adding another geom_point() layer. Note that we add additional geom_point() layers but they must go before our original geom_point layer or the orange dots will appear on top of the coloured points (which in this case would also be fine!).

There’s much more you can do with annotations. As usual, I’ll direct you to ggplot2: Elegant Graphics for Data Analysis: https://ggplot2-book.org/annotations.html#direct-labelling

Want a review? Try changing the colour palette of the points in this plot.

DAY 23

On the twenty-third day of Christmas…

…we introduced cowplot!!

“Cowplot????????:

Yes. Cowplot.

Cowplot is an add-on to ggplot and allows us to combine several plots into one.

“How is that different from faceting??”

Cowplot allows you to combine plots of different types into one image!

First, let’s install and load the cowplot package.

install.packages("cowplot")
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.2/cowplot_1.1.1.tgz'
Content type 'application/x-gzip' length 1373845 bytes (1.3 MB)
==================================================
downloaded 1.3 MB

The downloaded binary packages are in
    /var/folders/xp/d3s384tn7f16chkdxc5n6jh80000gn/T//RtmpnNy2R1/downloaded_packages

Now let’s make a few graphs.

sleigh.plot1 <-sleighs.subset %>%
  ggplot(aes(x=km_per_carrot, y=bag_space))+
  geom_point(colour="darkgreen")+
  theme_classic()

sleigh.plot2 <-sleighs.subset %>%
  ggplot(aes(x=bells))+
  geom_bar(fill="firebrick2")+
  theme_classic()

sleigh.plot3 <-sleighs.subset %>%
  ggplot(aes(x=km_per_carrot, y=deerpower))+
  geom_quantile(colour="gold3")+
  theme_classic()

tree.plot1<-trees %>%
  ggplot(aes(x=type, y=height))+
  geom_boxplot(fill="seagreen")+
  theme_classic()

tree.plot2 <-trees %>%
  ggplot(aes(x=xmas.magic))+
  geom_dotplot(fill="tomato3")+
  theme_classic()

Now we can use cowplot to create one image with multiple plots.

plot_grid(tree.plot1, tree.plot2, nrow=1, labels = c("A", "B"))
Bin width defaults to 1/30 of the range of the data. Pick better value with `binwidth`.

Let’s combine all 5 plots into one image.

plot_grid(tree.plot1, tree.plot2, sleigh.plot1, sleigh.plot2, sleigh.plot3, nrow = 2)
Bin width defaults to 1/30 of the range of the data. Pick better value with `binwidth`.
Smoothing formula not specified. Using: y ~ x

This looks okay, but maybe we wanted the tree plots to be on the top and the sleigh plots to be on the bottom. We can do that but specifying which ones go in the top row and which on the bottom.

Want a review? Try changing your axis labels to something cleaner and more informative (pretend you were going to publish this image in a paper!). Hint: you will need to edit the labels in your original plots, not in the plot_grid(). If you can’t remember how, check out day 10.

DAY 24

On the twenty-fourth day of Christmas…

…we put our knowledge to the test!

Today we will not learn anything new. Instead, we will bring together what we’ve learned to build some plots!

We will build three different plots:

  1. Build a plot using the trees dataset that shows violin plots by tree type, also coloured by tree type. Add a title, informative axis labels, and a legend at the bottom.

  2. Build a plot using the sleighs dataset that shows deerpower by weight, with a trendline, points coloured by km per carrot (with a size and shape where you can see the colour differences), and an informative title, axis labels, and legend.

  3. Build an image that shows the previous two graphs side-by-side in one image with labels A and B.

And as a little Christmas Eve gift, here is a fantastic cheatsheet for ggplot2. I keep it in my bookmarks bar :) https://github.com/rstudio/cheatsheets/blob/main/data-visualization-2.1.pdf - this might come in handy while you are working through these exercises!

DAY 25 - MERRY CHRISTMAS!!!

I hope you enjoyed this advent calendar. Similarly to the previous one, for day 25, I’ve given you code for a Christmas visual created by someone else (in this case, data scientist, Jodie Burchell). But unlike in the original R advent calendaR, now you should be able to understand a lot of the components and the grammar used in this code. Load the data and run the code to see what happens! The original blog post can be found here: https://t-redactyl.io/blog/2016/12/a-very-ggplot2-christmas.html

First, load in this dataset, which is available through git hub.

ChristmasTree <- read.csv("https://raw.githubusercontent.com/t-redactyl/Blog-posts/master/Christmas%20tree%20base%20data.csv")
# Generate the "lights"
Desired.Lights <- 50
Total.Lights <- sum(round(Desired.Lights * 0.35) + round(Desired.Lights * 0.20) + 
                      round(Desired.Lights * 0.17) + round(Desired.Lights * 0.13) +
                      round(Desired.Lights * 0.10) + round(Desired.Lights * 0.05))

Lights <- data.frame(Lights.X = c(round(runif(round(Desired.Lights * 0.35), 4, 18), 0),
                                       round(runif(round(Desired.Lights * 0.20), 5, 17), 0),
                                       round(runif(round(Desired.Lights * 0.17), 6, 16), 0),
                                       round(runif(round(Desired.Lights * 0.13), 7, 15), 0),
                                       round(runif(round(Desired.Lights * 0.10), 8, 14), 0),
                                       round(runif(round(Desired.Lights * 0.05), 10, 12), 0)))
Lights$Lights.Y <- c(round(runif(round(Desired.Lights * 0.35), 4, 6), 0),
                          round(runif(round(Desired.Lights * 0.20), 7, 8), 0),
                          round(runif(round(Desired.Lights * 0.17), 9, 10), 0),
                          round(runif(round(Desired.Lights * 0.13), 11, 12), 0),
                          round(runif(round(Desired.Lights * 0.10), 13, 14), 0),
                          round(runif(round(Desired.Lights * 0.05), 15, 17), 0))
Lights$Lights.Colour <- c(round(runif(Total.Lights, 1, 4), 0))

# Generate the "baubles"
Baubles <- data.frame(Bauble.X = c(6, 9, 15, 17, 5, 13, 16, 7, 10, 14, 7, 9, 11, 
                                   14, 8, 14, 9, 12, 11, 12, 14, 11, 17, 10))
Baubles$Bauble.Y <- c(4, 5, 4, 4, 5, 5, 5, 6, 6, 6, 8, 8, 8, 8, 10,
                      10, 11, 11, 12, 13, 10, 16, 7, 14)
Baubles$Bauble.Colour <- factor(c(1, 2, 2, 3, 2, 3, 1, 3, 1, 1, 1, 2, 1, 2,
                                  3, 3, 2, 1, 3, 2, 1, 3, 3, 1))
Baubles$Bauble.Size <- c(1, 3, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 3, 3, 3,
                         2, 3, 1, 1, 2, 2, 3, 3, 2)

# Generate the plot
ggplot() + 
  geom_tile(data = ChristmasTree, aes(x = Tree.X, y = Tree.Y, fill = Tree.Colour)) +
  scale_fill_identity() + 
  geom_point(data = Lights, aes(x = Lights.X, y = Lights.Y, alpha = Lights.Colour),
             colour = "lightgoldenrodyellow", shape = 16) +
  geom_point(data = Baubles, aes(x = Bauble.X, y = Bauble.Y, colour = Bauble.Colour, size = Bauble.Size),
             shape = 16) +
  scale_colour_manual(values = c("firebrick2", "gold", "dodgerblue3")) +
  scale_size_area(max_size = 12) +
  theme_bw() +
  scale_x_continuous(breaks = NULL) + 
  scale_y_continuous(breaks = NULL) +
  geom_segment(aes(x = 2.5, xend = 4.5, y = 1.5, yend = 1.5), colour = "blueviolet", size = 2) +
  geom_segment(aes(x = 5.5, xend = 8.5, y = 1.5, yend = 1.5), colour = "dodgerblue3", size = 2) +
  geom_segment(aes(x = 13.5, xend = 16.5, y = 1.5, yend = 1.5), colour = "blueviolet", size = 2) +
  geom_segment(aes(x = 17.5, xend = 19.5, y = 1.5, yend = 1.5), colour = "dodgerblue3", size = 2) +
  geom_segment(aes(x = 3.5, xend = 3.5, y = 0.5, yend = 2.5), colour = "blueviolet", size = 2) +
  geom_segment(aes(x = 7.0, xend = 7.0, y = 0.5, yend = 2.5), colour = "dodgerblue3", size = 2) +
  geom_segment(aes(x = 15.0, xend = 15.0, y = 0.5, yend = 2.5), colour = "blueviolet", size = 2) +
  geom_segment(aes(x = 18.5, xend = 18.5, y = 0.5, yend = 2.5), colour = "dodgerblue3", size = 2) +
  annotate("text", x = 11, y = 20, label = "Merry Christmas!",
           size = 12) +
  labs(x = "", y = "") +
  theme(legend.position = "none")

LS0tCnRpdGxlOiAiVGhlIGdncGxvdCBhZHZlbnQgY2FsZW5kYVIhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpIZXJlIGl0IGlzOiB0aGUgZ2dwbG90IGFkdmVudCBjYWxlbmRhUiEgVGhlICJnZyIgaW4gZ2dwbG90IHJlZmVycyB0byB0aGUgZ3JhbW1hciBvZiBncmFwaGljcy4gRm9yIHRoZSBuZXh0IDI1IGRheXMsIHdlIHdpbGwgZ28gdGhyb3VnaCBhbiBpbnRyb2R1Y3Rpb24gdG8gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MsIG1ha2UgYSBsb3Qgb2YgdmlzdWFsaXphdGlvbnMgKHNvbWUgZ29vZCwgc29tZSBiYWQpLCBhbmQgbGVhcm4gc29tZSBvZiB0aGUgYmFzaWMgZnVuY3Rpb25zIGFuZCBmZWF0dXJlcyBvZiB0aGUgZ2dwbG90MiBwYWNrYWdlLiAqKk5PVEUqKiBJIGFtIG5vIGV4cGVydCBpbiBnZ3Bsb3QuIEkgbGl0ZXJhbGx5IGxlYXJuZWQgd2hpbGUgY3JlYXRpbmcgdGhpcyB0dXRvcmlhbCwgYW5kIHRoYXQgd2FzIGEgYmlnIG1vdGl2YXRpb24gaW4gZG9pbmcgdGhpcy4gSWYgdGhlcmUgYXJlIGVycm9ycyBvciBzbW9vdGhlciB3YXlzIHRvIGRvIHRoZSBzYW1lIHRoaW5nLCBwbGVhc2UgbGV0IG1lIGtub3chIFIgaXMgYWJvdXQgY29uc3RhbnQgbGVhcm5pbmcgYW5kIGltcHJvdmluZywgZ2dwbG90IGlzIG5vIGRpZmZlcmVudC4KCkRBWSAxCgpPbiB0aGUgZmlyc3QgZGF5IG9mIENocmlzdG1hcy4uLiB3ZSdyZSBqdW1waW5nIGludG8gdGhlIHRpZHl2ZXJzZSEKCmdncGxvdCBpcyBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UsIGEgZ3JvdXAgb2YgcGFja2FnZXMgdGhhdCBhbHNvIGluY2x1ZGVzIGRwbHlyLCByZWFkciwgYW5kIG90aGVyIHZlcnkgaGVscGZ1bCBwYWNrYWdlcyB0aGF0IHlvdSBzaG91bGQgaGF2ZSEgWW91IGNhbiBpbnN0YWxsIGFuZCBsb2FkIGdncGxvdCBzZXBhcmF0ZWx5LCBidXTigKYgd2h5PyAoOgoKVGhlIHBhY2thZ2Ugd2UncmUgdXNpbmcgaXMgYWN0dWFsbHkgY2FsbGVkIGdncGxvdDIuIFN1cGVyLWR1cGVyIGZ1biBmYWN0OiAiZ2dwbG90MiBpcyBjYWxsZWQgZ2dwbG90MiBiZWNhdXNlIG9uY2UgdXBvbiBhIHRpbWUgdGhlcmUgd2FzIGp1c3QgYSBsaWJyYXJ5IGdncGxvdC4gSG93ZXZlciwgdGhlIGRldmVsb3BlciBub3RpY2VkIHRoYXQgaXQgdXNlZCBhbiBpbmVmZmljaWVudCBzZXQgb2YgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3Igbm90IHRvIGJyZWFrIHRoZSBBUEksIHRoZSBhdXRob3JzIGludHJvZHVjZWQgYSBzdWNjZXNzb3IgcGFja2FnZSBnZ3Bsb3QyLiBIb3dldmVyLCB0aGUgY2VudHJhbCBmdW5jdGlvbiBpbiB0aGlzIHBhY2thZ2UgaXMgc3RpbGwgY2FsbGVkIGdncGxvdCgpLCBub3QgZ2dwbG90MigpISIgKHdhc24ndCB0aGF0IGZ1bj8gU291cmNlOiBGcmVlbWFuICYgUm9zcywgMjAxOSkuCgpJbnN0YWxsIGFuZCBsb2FkIHRpZHl2ZXJzZToKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCkxldCdzIGFsc28gbG9hZCBvdXIgZGF0YSB1c2luZyBhbm90aGVyIHRpZHl2ZXJzZSBwYWNrYWdlLCByZWFkciwgYW5kIHRoZW4gdmlldyB0aGUgZGF0YS4gV2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggdHdvIGRhdGFzZXRzIHRocm91Z2hvdXQgdGhpcyB0dXRvcmlhbDoKYGBge3J9CnNsZWlnaHMgPC1yZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2tpaXJzdGkvZ2dwbG90X2FkdmVudGNhbGVuZGFSL21haW4vc2xlaWdoLmRhdGEuY3N2IikKc2xlaWdocyAjaWYgeW91IGhpZ2hsaWdodCBvciByZXR5cGUgYW5kIHJ1biB0aGUgbmFtZSAic2xlaWdocyIgaXQgd2lsbCBzaG93IHlvdSBhIHNhbXBsZSBvZiB0aGUgZGF0YQoKdHJlZXMgPC1yZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2tpaXJzdGkvZ2dwbG90X2FkdmVudGNhbGVuZGFSL21haW4veG1hcy50cmVlcy5jc3YiKQp0cmVlcwpgYGAKVXNpbmcgcmVhZHIgYW5kIHJlYWRfY3N2KCksIGluc3RlYWQgb2YgcmVhZC5jc3YoKSBmcm9tIGJhc2UgUiwgbG9hZHMgb3VyIGRhdGEgaW4gYXMgdGliYmxlcy4gV2h5IHVzZSB0aWJibGVzIG92ZXIgdHJhZGl0aW9uYWwgZGF0YWZyYW1lcz8gVGhyZWUgcmVhc29uczogKDEpIHRoZSBpbnB1dCB0eXBlcyBhcmVuJ3QgYXV0b21hdGljYWxseSBjaGFuZ2VkIHdoZW4geW91IHJlYWQgaW4gdGhlIGRhdGEsICgyKSB5b3UgY2FuIGtlZXAgbGlzdHMgYXMgY29sdW1ucywgYW5kICgzKSB5b3UgY2FuIHVzZSBub24tc3RhbmRhcmQgdmFyaWFibGUgbmFtZXMgKGUuZy4sIHN0YXJ0aW5nIHdpdGggYSBudW1iZXIsIGFzIGluICIxc3RfcGxhY2UiKS4gVGhhbmsgeW91IHRvIG15IGZyaWVuZCBQYXQgZm9yIHRoaXMgZXhwbGFuYXRpb24hCgpEQVkgMgoKT24gdGhlIHNlY29uZCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBsZWFybmVkIHRoZSBsYW5ndWFnZSBvZiBnZ3Bsb3QgYWthIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzISBnZ3Bsb3QgaXMgZGlmZmVyZW50IGZyb20gYmFzZSBSIGdyYXBoaWNzLiBIb3c/IEJhc2UgUiBncmFwaGljcyB3b3JrIG9uIHRoZSBpbmRpdmlkdWFsIHZlY3RvcnMsIGdncGxvdCB3b3JrcyBvbiBkYXRhZnJhbWVzIChTb3VyY2U6IFByYWJoYWthcmFuLCAyMDE3KS4gZ2dwbG90IHdvcmtzIGJ5IGFkZGluZyBsYXllciB1cG9uIGxheWVyIHRvIGNyZWF0ZSB5b3VyIHZpc3VhbGl6YXRpb24uIEluIGJhc2UgUiBncmFwaGljcywgeW91IHB1dCBhbGwgdGhlIGluZm9ybWF0aW9uIGluIG9uZSBjb2RlIGFuZCBpdCBzcGl0cyBvdXQgYSBncmFwaGljLiBJbiBnZ3Bsb3QsIHlvdSBidWlsZCB5b3VyIGdyYWhwaWMgd2l0aCBsYXllcnMuCgpXaGF0IGFyZSBzb21lIG9mIHRoZSBkaWZmZXJlbnQgY29tcG9uZW50cyBvZiBhIGdyYXBoaWMgKFNvdXJjZTogRnJlZW1hbiAmIFJvc3MsIDIwMTkpPwotIGRhdGEKLSBnZW9tZXRyaWMgb2JqZWN0cyAoZ2VvbXMpCi0gYWVzdGhldGljcwotIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9ucwotIHBvc2l0aW9uIGFkanVzdG1lbnRzCi0gc2NhbGUKLSBjb29yZGluYXRlIHN5c3RlbQotIGZhY2V0cwotIHRoZW1lcwoKVGhlIGZpcnN0IGxheWVyIG9mIGdncGxvdCBpcyBhbHdheXMuLi5nZ3Bsb3QuIElmIHlvdSBydW4gdGhlIGNvZGUgYmVsb3csIHlvdSB3aWxsIGdldCBhIGJsYW5rIGdyYXBoaWMgd2l0aCBhIGdyZXkgYmFja2dyb3VuZC4gVGhlIGdyZXkgYmFja2dyb3VuZCBpcyB0aGUgZ2dwbG90IGRlZmF1bHQuIEluIGJhc2UgUiwgeW91IGNhbid0IHJ1biBqdXN0IGZ1bmN0aW9uLCBlLmcuLCBib3hwbG90KCksIHdpdGhvdXQgYSB2ZWN0b3IgaW4gdGhlIGJyYWNrZXRzLgoKYGBge3J9CmdncGxvdCgpCmBgYApOb3cgbGV0J3MgYWRkIGFub3RoZXIgbGF5ZXIgdG8gZ2dwbG90LiBXZSdsbCBzcGVjaWZ5IHRoZSBkYXRhc2V0IGFuZCB3aGF0IHdlIHdhbnQgb3VyIHggYW5kIHkgYXhlcyB0byBiZS4KYGBge3J9CmdncGxvdCh0cmVlcywgYWVzKHg9dHlwZSwgeT1oZWlnaHQpKQpgYGAKV2hlbiB5b3UgcnVuIHRoaXMgY29kZSwgeW91IHNob3VsZCBzZWUgdGhhdCB3ZSBub3cgaGF2ZSBheGVzIGFuZCBsYWJlbHMgb24gdG9wIG9mIG91ciBncmV5IGJhY2tncm91bmQuIE5vdGUsIHlvdSBjYW4gZHJvcCB0aGUgeD0gYW5kIHk9IGFuZCB0aGUgY29kZSB3aWxsIHJ1biB0aGUgc2FtZSAoZG9uJ3QgdGFrZSBteSB3b3JkIGZvciBpdCwgdHJ5IGl0IHlvdXJzZWxmISkuCgoKREFZIDMKCk9uIHRoZSB0aGlyZCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBhZGRlZCBkYXRhIHRvIG91ciBncmFwaGljIGFuZCBleHBsb3JlZCBnZW9tIGxheWVycyEKCkdlb21zLCBvciBnZW9tZXRyaWMgb2JqZWN0cywgYXJlIGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbnMgb2YgdGhlIGRhdGEuIFRoZXJlIGFyZSBtYW55IG1hbnkgdHlwZXMgb2YgZ2VvbXMgKGhlcmUncyBhIGxvbmcgbGlzdCBvZiBleGFtcGxlczogaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlLyNnZW9tcykuIExldCdzIHRyeSBhIGZldy4KCkdlb20gbGF5ZXJzIHN0YXJ0IHdpdGggImdlb21fIiBhbmQgYXJlIGZvbGxvd2VkIGJ5IHRoZSB0eXBlIG9mIGdlb21ldHJpYyBvYmplY3QsIGUuZy4sICJnZW9tX3BvaW50IiBvciAiZ2VvbV9saW5lIi4gQmVjYXVzZSB3ZSdyZSBhZGRpbmcgYSBuZXcgbGF5ZXIgb250byBvdXIgZ3JhcGhpYywgd2UgdXNlIGEgKyBhZnRlciBvdXIgZmlyc3QgbGluZSBvZiBjb2RlLiBUbyBrZWVwIHRoaW5ncyB0aWR5IGFuZCBlYXN5IHRvIHJlYWQsIEkgdXN1YWxseSBzdGFydCBteSBuZXcgbGF5ZXIgb24gYSBuZXcgbGluZS4KCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9wb2ludCgpCmBgYApTaW5jZSB3ZSBhcmUgd29ya2luZyB3aXRoIGNhdGVnb3JpY2FsIGRhdGEgKHR5cGVzIG9mIHRyZWVzKSwgdGhlIHBvaW50cyBhbGwgZmFsbCBvbiB0aHJlZSBsaW5lcy4gTGV0J3MgY2hhbmdlIG91ciB4IGFuZCB5IGF4ZXMgYmVmb3JlIG1vdmluZyBvbiB0byBhIGRpZmZlcmVudCB0eXBlIG9mIGdlb20uCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeD14bWFzLm1hZ2ljLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoKQpgYGAKTm93IHdlJ3JlIGNvbXBhcmluZyB0d28gY29udGludW91cyB2YXJpYWJsZXMgKHhtYXMgbWFnaWMgYW5kIHRyZWUgaGVpZ2h0KSBzbyB0aGUgcG9pbnRzIGFyZSBzY2F0dGVyZWQgYWNyb3NzIG91ciBncmFwaGljLgoKTGV0J3MgbG9vayBhdCBzb21lIGRpZmZlcmVudCBnZW9tIHR5cGVzLiBXZSdsbCBnbyBiYWNrIHRvIHR5cGUgdnMuIGhlaWdodCBmb3IgdGhpcyBvbmUuCgpgYGB7cn0KZ2dwbG90KHRyZWVzLCBhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdCgpCmBgYApXaGVuIGNyZWF0aW5nIGdyYXBoaWNzLCBhbHdheXMgY29uc2lkZXIgdGhlIHR5cGUgb2YgZGF0YSB5b3UncmUgd29ya2luZyB3aXRoIChlLmcuLCBjb250aW51b3VzIHZzLiBkaXNjcmV0ZSkuIFRoZSB0eXBlIG9mIGRhdGEgeW91J3JlIHdvcmtpbmcgd2l0aCBzaG91bGQgZGV0ZXJtaW5lIHRoZSB0eXBlIG9mIGdlb20geW91IGNob29zZS4gU29tZSBnZW9tcyB3b24ndCBydW4gcHJvcGVybHkgaWYgdGhlIHR5cGUgb2YgZGF0YSB5b3UncmUgaW5wdXR0aW5nIGRvZXNuJ3Qgd29yayB3aXRoIHRob3NlIGRhdGEgdHlwZXMuCgpPbmUgbW9yZSBleGFtcGxlIG9mIGEgZ2VvbSBsYXllciB1c2luZyB0aGUgdHJlZXMgZGF0YS4KCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV92aW9saW4oKQpgYGAKCkRBWSA0CgpPbiB0aGUgZm91cnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGludHJvZHVjZWQgcGlwZXMhCgpUaGlzIGlzIGEgYml0IG9mIGEgc2VndWUgZnJvbSBnZ3Bsb3QgYW5kIHlvdSBjZXJ0YWlubHkgZG9uJ3QgbmVlZCB0byB1c2UgcGlwZXMgdG8gY3JlYXRlIGJlYXV0aWZ1bCBnZ3Bsb3QgZ3JhcGhpY3MgQlVUIGl0IG1pZ2h0IG1ha2UgeW91ciBkYXRhIGxvb2sgbW9yZSB0aWR5LgoKVGhlIHBpcGUgb3BlcmF0b3IgKCU+JSkgaXMgaW5jbHVkZWQgaW4gdGhlIHRpZHl2ZXJzZSwgc28gaWYgeW91IGxvYWRlZCB0aGUgdGlkeXZlcnNlLCB0aGVuIHlvdSd2ZSBnb3QgYWNjZXNzIHRvICU+JS4gRnJvbSBSIGZvciBEYXRhIFNjaWVuY2UsICJQaXBlcyBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBjbGVhcmx5IGV4cHJlc3NpbmcgYSBzZXF1ZW5jZSBvZiBtdWx0aXBsZSBvcGVyYXRpb25zLi4uVGhlIHBvaW50IG9mIHRoZSBwaXBlIGlzIHRvIGhlbHAgeW91IHdyaXRlIGNvZGUgaW4gYSB3YXkgdGhhdCBpcyBlYXNpZXIgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZC4iIEluIHdvcmRzLCAlPiUgbWVhbnMgImFuZCB0aGVuIi4gQnkgdXNpbmcgYSBwaXBlLCB5b3UncmUgdGVsbGluZyBSIHRvIHJ1biB0aGUgZmlyc3QgbGluZSBvZiBjb2RlIEFORCBUSEVOIHJ1biB0aGUgbmV4dCBsaW5lIG9mIGNvZGUuCgpXaHkgZG8gSSBicmluZyB0aGlzIHVwIG5vdz8gUHJldmlvdXNseSwgd2UgcmFuIHRoaXMgY29kZSB0byBwcm9kdWNlIGEgYm94cGxvdCBzaG93aW5nIHRyZWUgaGVpZ2h0czoKCmBgYHtyfQpnZ3Bsb3QodHJlZXMsIGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpPZnRlbiwgaW4gb25saW5lIGV4YW1wbGVzIGFuZCB0dXRvcmlhbHMsIHlvdSB3aWxsIHNlZSBnZ3Bsb3QgY29kZXMgd3JpdHRlbiB3aXRoIHRoZSBkYXRhZnJhbWUgbGlzdGVkIGZpcnN0LCB0aGVuIGEgcGlwZSBsZWFkaW5nIGludG8gdGhlIGdncGxvdCBjb2RlLiBJZiB5b3UgcnVuIHRoZSBjb2RlIGJlbG93LCB5b3UnbGwgc2VlIHRoYXQgd2UgY2FuIHByb2R1Y2UgdGhlIGV4YWN0IHNhbWUgZ3JhcGhpYzoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoKQpgYGAKQW5vdGhlciB0aXAgZnJvbSBteSBmcmllbmQgUGF0LCAiVGhlIG1vc3QgdXNlZnVsIGFwcGxpY2F0aW9uIG9mIHRoZSBwaXBlIGlzIHRvIHBsdWcgdGhlIHJlc3VsdCBvZiBvbmUgZnVuY3Rpb24gaW50byBhbm90aGVyIHdpdGhvdXQgY3JlYXRpbmcgaW50ZXJtZWRpYXRlIGRhdGEgZnJhbWVzIiBIZXJlJ3MgYW4gZXhhbXBsZSBvZiB3aGF0IHRoaXMgbWlnaHQgbG9vayBsaWtlOgoKbXlfZGF0YSAlPiUKICBmdW5jdGlvbjEoYXJndW1lbnRzLCBldGMpICU+JQogIGZ1bmN0aW9uMihhcmd1bWVudHMsIGV0YykKCgpEQVkgNQoKT24gdGhlIGZpZnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIHN0YXJ0ZWQgd29ya2luZyB3aXRoIHRoZSBhZXN0aGV0aWNzIG9mIG91ciBncmFwaGljIQoKQWVzdGhldGljIG1hcHBpbmcgZGVzY3JpYmVzIHRoZSB2aXN1YWwgcHJvcGVydGllcyBvZiB0aGUgZ3JhcGhpYy4gV2UgY2FuIGNoYW5nZSB0aGUgYWVzdGhldGljcyBvZiBnZ3Bsb3QoKSBvciBlYWNoIGxheWVyIHdlIGFkZCB0byBpdC4gV2UgdXNlIGFlcygpIGFuZCB0aGVuIGN1c3RvbWl6ZSBvdXIgZ3JhcGhpYyB0byBhcHBlYXIgaG93IHdlJ2QgbGlrZSBpdC4gT24gRGF5IDMsIHdlIGNyZWF0ZWQgc29tZSBwcmV0dHkgYm9yaW5nIGdyYXBoczogd2hpdGUgYm94ZXMgb3IgdmlvbGlucywgYmxhY2sgZG90cywgYW5kIGdyZXkgYmFja2dyb3VuZHMuIFdoaWxlIHRoaXMgaXMgZmluZSBmb3IgZXhwbG9yaW5nIGRhdGEsIGl0J3MgcGVyaGFwcyBub3QgaG93IHdlIHdhbnQgb3VyIGZpbmlzaGVkIHByb2R1Y3QgdG8gbG9vayEKClRvZGF5LCB3ZSdyZSBnb2luZyB0byB3b3JrIG9uIGNoYW5naW5nIHRoZSBjb2xvdXJzIG9mIG91ciBkYXRhLiBMZXQncyBzdGFydCB3aXRoIHRoZSBzY2F0dGVycGxvdCwgaS5lLiwgdGhlIGdlb21fcG9pbnQoKSBncmFwaGljLgoKVG8gY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBnZW9tX3BvaW50KCkgZG90cywgd2UgYWRkICJhZXMoKSIgd2l0aGluIHRoZSBwYXJlbnRoZXNlcyBvZiBnZW9tX3BvaW50KCkuIFlvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgd2UgYWxyZWFkeSB1c2VkICJhZXMoKSIgd2hlbiB3ZSB0b2xkIGdncGxvdCB3aGF0IHdlIHdhbnRlZCBvdXIgWCBhbmQgWSBheGVzIHRvIGJlLiBOb3cgd2Ugd2lsbCBhbHNvIHRlbGwgZ2dwbG90IHRvIGFzc2lnbiBkaWZmZXJlbnQgY29sb3VycyB0byBvdXIgcG9pbnRzIGJ5IHRyZWUgdHlwZS4gSW4gdGhpcyBjYXNlLCB3ZSBhcmUgbm90IGNob29zaW5nIHRoZSBjb2xvdXJzLgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQsIGNvbG91cj10eXBlKSkrCiAgZ2VvbV9wb2ludCgpCmBgYApOb3cgaW5zdGVhZCBvZiBjaGFuZ2luZyB0aGUgYWVzdGhldGljcyBvZiB0aGUgZ2dwbG90KCkgbGF5ZXIsIGxldCdzIGluc3RlYWQgY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBnZW9tX3BvaW50KCkgbGF5ZXIuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KCkrCiAgZ2VvbV9wb2ludChhZXMoeD10eXBlLCB5PWhlaWdodCwgY29sb3VyPXR5cGUpKQpgYGAKTG9va3MgdGhlIHNhbWUgcmlnaHQ/IFNvIHdoYXQncyB0aGUgZGlmZmVyZW5jZT8gUmlnaHQgbm93LCBub3RoaW5nIGFwcGVhcnMgZGlmZmVyZW50LCBidXQgbGF0ZXIgaXQgbWF5IGltcGFjdCBob3cgeW91ciBncmFwaGljIGxvb2tzLiBXaGVuIHlvdSBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2YgdGhlIGdncGxvdCgpIGxheWVyLCB0aG9zZSBhZXN0aGV0aWNzIHdpbGwgYmUgYXBwbGllZCBhcyB0aGUgZGVmYXVsdCB0byBhbGwgb2YgeW91ciBzdWJzZXF1ZW50IGxheWVycy4gSWYgeW91IGNoYW5nZSB0aGUgYWVzdGhldGljcyBvZiBhbiBpbmRpdmlkdWFsIGxheWVyLCBpdCB3aWxsIG9ubHkgYmUgYXBwbGllZCB0byB0aGF0IGxheWVyIGFuZCBpdCB3aWxsIG92ZXJyaWRlIHRoZSBkZWZhdWx0LgoKV2hhdCBpZiB3ZSB3YW50IHRvIHVzZSBhbGwgdGhlIHNhbWUgY29sb3VyIGZvciBvdXIgcG9pbnRzPyBGaW5kIG91dCB0b21vcnJvdyEKCkRBWSA2CgpPbiB0aGUgc2l4dGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgY29udGludWVkIHdpdGggYWVzdGhldGljIG1hcHBpbmcgYW5kIGNvbG91cnMhCgpTbyB5b3Ugd2FudCBhbGwgeW91ciBwb2ludHMgdG8gYmUgb25lIGNvbG91ci4gU291bmRzIGVhc3ktcGVhc3kgcmlnaHQ/IE5vdCBxdWl0ZS4KCkhlcmUncyB3aHkgaXQncyBhIGJpdCBjb25mdXNpbmcuIEkgaGFkIHRvIGRvIGEgYml0IG9mIGRpZ2dpbmcgdG8gdW5kZXJzdGFuZCBleGFjdGx5IHdoeSB0aGlzIGlzLi4uCgpXaGVuIHdlIHdhbnQgdG8gbWFwIGEgdmFyaWFibGUgb2Ygb3VyIGRhdGEgKGUuZy4sIHRlbGxpbmcgZ2dwbG90IHdlIHdhbnQgdG8gY29sb3VyIGJ5IHRyZWUgdHlwZSksIHdlIHB1dCBhZXMoKSBpbnNpZGUgdGhlIGdlb21fcG9pbnQoKS4gSWYgd2Ugd2FudCB0byBhcHBseSBhIGNvbnN0YW50IGNvbG91ciAoY29uc3RhbnQgdmFsdWUpIHRvIG91ciBwb2ludHMgKGUuZy4sIHRlbGxpbmcgZ2dwbG90IHdlIHdhbnQgYWxsIG91ciBwb2ludHMgdG8gYmUgYmx1ZSksIHdlIHB1dCBhZXMoKSBPVVRTSURFIGdlb21fcG9pbnQpLiBUcnkgYm90aCB0aGUgY29kZXMgYmVsb3cgdG8gc2VlIHdoYXQgaGFwcGVucy4gCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj0iYmx1ZSIpKQoKdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKCksIGNvbG91cj0iYmx1ZSIpCmBgYApOb3RlOiBpbiB0aGlzIGNhc2UgeW91IGNvdWxkIGFsc28gbGVhdmUgb3V0IHRoZSBhZXMoKSB3aXRoaW4gZ2VvbV9wb2ludCgpIGFuZCBpdCB3b3VsZCBydW4gdGhlIHNhbWUuIAoKRm9yIG1vcmUgb2YgYW4gZXhwbGFuYXRpb24gb24gd2h5IGFlcygpIHdvcmtzIHRoaXMgd2F5LCB5b3UgY2FuIGNoZWNrIG91dCB0aGlzIGhlbHBmdWwgdGhyZWFkIG9uIHN0YWNrb3ZlcmZsb3cgdGhhdCBoZWxwZWQgbWU6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQxODYzMDQ5L3doZW4tZG9lcy10aGUtYXJndW1lbnQtZ28taW5zaWRlLW9yLW91dHNpZGUtYWVzLiBIZXJlJ3MgYW5vdGhlciByZWFsbHkgZmFudGFzdGljIHJlc291cmNlOiBodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzFEdnVsMXA2VFlINmdXSnpaUndwRTBZWDFkTzBoREYtYi92aWV3LgoKREFZIDcKCk9uIHRoZSBzZXZlbnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGNoYW5nZWQgdGhlIGNvbG91cnMgb2YgYm94cGxvdHMhCgpUcnkgcnVubmluZyB0aGUgc2FtZSBjb2RlIGZyb20geWVzdGVyZGF5IGJ1dCBvbiBhIGdlb21fYm94cGxvdCgpIGdyYXBoaWMuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcygpLCBjb2xvdXI9ImJsdWUiKQpgYGAKSG1tLi4uIG9rYXkuIEJ1dCB3aGF0IGlmIHdlIHdhbnRlZCB0aGUgaW5zaWRlIG9mIHRoZSBib3hlcyB0byBiZSBjb2xvdXJlZCwgbm90IHRoZSBvdXRsaW5lPwoKSW5zdGVhZCBvZiAiY29sb3VyID0iIHdlIHVzZSAiZmlsbCA9IiBpbnN0ZWFkLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoKSwgZmlsbD0iYmx1ZSIpCmBgYAoKVGhpcyB3b3JrcyBmb3IgcG9pbnRzIHRvby4gVGhlIGRlZmF1bHQgZm9yIGdlb21fcG9pbnQoKSBhcmUgc29saWQgcG9pbnRzLCBidXQgeW91IGNhbiBjaGFuZ2UgdGhlc2UgdG8gcG9pbnRzIHdpdGggZGlmZmVyZW50IG91dGxpbmUgYW5kIGZpbGwgY29sb3Vycy4gCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9wb2ludChhZXMoKSwgc2hhcGUgPSAyMSwgZmlsbD0iZ3JlZW40IiwgY29sb3VyPSJyZWQiKQpgYGAKV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBjb2xvdXJzIG9mIG91ciBib3hwbG90cyBieSB0cmVlIHR5cGUsIGFzIHdlIGRpZCB3aXRoIHRoZSBwb2ludHMgb24gRGF5IDUuIFJlbWVtYmVyIGZyb20geWVzdGVyZGF5IHRoYXQgYmVjYXVzZSB3ZSB3YW50IHRvIGNoYW5nZSB0aGUgY29sb3VycyBieSBhIHZhcmlhYmxlIChpLmUuLCBtdWx0aXBsZSBjb2xvdXJzIGRldGVybWluZCBieSB0aGUgbGV2ZWxzIG9mIHRoZSB2YXJpYWJsZSkgcmF0aGVyIHRoYW4gY2hhbmdlIHVzaW5nIGEgY29uc3RhbnQgdmFsdWUgKGkuZS4sIHNpbmdsZSBjb2xvdXIpLCB3ZSBwdXQgdGhpcyBJTlNJREUgYWVzKCkuCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpCmBgYAoKTm93IHRoYXQgd2UncmUgcGxheWluZyBhcm91bmQgd2l0aCBjb2xvdXJzLiBJdCdzIHRpbWUgSSBpbnRyb2R1Y2UgeW91IHRvIGEgZ29vZCBjb2xvdXIgcmVzb3VyY2U6IGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmLiBUaGVyZSBhcmUgbWFueSBncmVhdCBvbmxpbmUgcmVzb3VyY2VzIGFib3V0IGNvbG91cnMsIGJ1dCBJIGxpa2UgdGhpcyBvbmUgbWFkZSBieSBEci4gWWluZyBXZWkuCgpEQVkgOAoKT24gdGhlIGVpZ2h0IGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGZpbmFsbHkgdG9vayBhIGJyZWFrIGZyb20gdGFsa2luZyBhYm91dCBjb2xvdXJzIQoKQ29sb3VyIGlzIGEgYmlnIG9uZSwgYnV0IGxldCdzIHdvcmsgd2l0aCBzb21lIG90aGVyIGNoYW5nZXMgdG8gdGhlIGFlc3RoZXRpY3Mgb2Ygb3VyIGdyYXBoaWMuIExldCdzIGJyaW5nIGJhY2sgdGhlIGdlb21fcG9pbnQoKSBncmFwaGljLiBJJ20gdXNpbmcgdGhlIHRoZW1lIG9mIENocmlzdG1hcywgYnV0IG9mIGNvdXJzZSwgRGVjZW1iZXIgaGFzIG1hbnksIG1hbnkgaG9saWRheXMuIEhhbnVra2FoIHN0YXJ0cyBvbiBEZWMuIDE3IHRoaXMgeWVhciwgc28gbGV0J3MgbWFrZSBhIEhhbnVra2FoLWluc3BpcmVkIGdyYXBoaWMgYW5kIGxvb2sgYXQgZGlmZmVyZW50IHdheXMgdG8gY2hhbmdlIHRoZSBhZXN0aGV0aWNzIG9mIG91ciBncmFwaHMuCgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX3BvaW50KGFlcygpLCBzaGFwZSA9IDExLCBjb2xvdXI9ImJsdWUiLCBzaXplID0gNiwgc3Ryb2tlPTIpCmBgYAoKSGVyZSB3ZSd2ZSBzcGVjaWZpZWQgdGhlIHNoYXBlLCB0aGUgY29sb3VyLCB0aGUgc2l6ZSwgYW5kIHRoZSBzdHJva2UgKGxpbmUgdGhpY2tuZXNzKSBvZiB0aGUgcG9pbnRzLiBUaGVyZSBhcmUgbWFueSBjaGFuZ2VzIHdlIGNhbiBtYWtlIHRvIGFlc3RoZXRpY3MsIHRoZXNlIGFyZSBqdXN0IGEgZmV3IGV4YW1wbGVzLiBMZXQncyB0YWtlIGEgbG9vayBhdCBhIGxpbmUgZ3JhcGggYW5kIGhvdyB3ZSBjYW4gY3VzdG9taXplIHRoYXQuCgpXZSBoYXZlbid0IG1hZGUgYSBsaW5lIGdyYXBoIHlldCwgc28gbGV0J3MgY3JlYXRlIGEgc2ltcGxlIG9uZSBmaXJzdCwgYmVmb3JlIHdlIGN1c3RvbWl6ZSBpdC4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKCkKYGBgCkhlcmUgd2UndmUgc3BlY2lmaWVkIHRoYXQgd2Ugd2FudCBlYWNoIGxpbmUgdG8gcmVwcmVzZW50IGEgZGlmZmVyZW50IHRyZWUgdHlwZSBieSB1c2luZyAiZ3JvdXA9Ii4gVGhpcyBnb2VzIGluc2lkZSBhZXMoKSBiZWNhdXNlIGl0IGlzIGJlaW5nIGFwcGxpZWQgdG8gdGhlIGRhdGEgKGkuZS4sIGl0IGNhbid0IGJlIGRvbmUgd2l0aG91dCB0aGlzIHNwZWNpZmljIGRhdGFzZXQpCgpOb3cgbGV0J3Mgc3BydWNlIGl0IHVwLiAoInNwcnVjZSIgaXQgdXAuLi4gYmVjYXVzZSB0aGV5J3JlIHRyZWVzLi4uIGhhaGFoYWhhLiBBIGxpdHRsZSBDaHJpc3RtYXMgY2hlZXIgZm9yIHlvdSkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKQpgYGAKV2hhdCBpZiB3ZSB3YW50IHRvIHNob3cgdGhlIHBvaW50cyBBTkQgdGhlIGxpbmVzPyBUb21vcnJvdyB3ZSB3aWxsIGFkZCBhbm90aGVyIGxheWVyIHRvIG91ciBncmFwaGljLgoKREFZIDkKCk9uIHRoZSBuaW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgYWRkZWQgYW5vdGhlciBnZW9tXyBsYXllciB0byBvdXIgZ3JhcGhpYy4KCkhlcmUncyBvdXIgZ2VvbV9saW4oKSBncmFwaGljIGZyb20geWVzdGVyZGF5OgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD1oZWlnaHQsIHk9eG1hcy5tYWdpYywgZ3JvdXA9dHlwZSkpKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpLCBsaW5ldHlwZT0iZGFzaGVkIikKYGBgCgpOb3cgbGV0J3MgYWRkIHBvaW50cyB0byBpdCBhcyB3ZWxsLiBJdCdzIGFzIHNpbXBsZSBhcyArIGdlb21fcG9pbnQoKSEKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUoYWVzKGNvbG91cj10eXBlKSwgbGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoKQpgYGAKCkFuZCBpZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2YgdGhvc2UgcG9pbnRzLCB3aGVyZSBkbyB5b3UgZG8gaXQ/IFdpdGhpbiBnZW9tX3BvaW50KCkhCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemUgPSAzLCBzaGFwZSA9IDgpCmBgYAoKVGhleSBraW5kIG9mIGxvb2sgbGlrZSBDaHJpc3RtYXMgbGlnaHRzIDopCgpEQVkgMTAKCk9uIHRoZSB0ZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBlZGl0ZWQgdGhlIHRleHQgb2Ygb3VyIGdyYXBoaWNzLgoKRWRpdGluZyB0aGUgbG9vayBvZiB5b3VyIHRleHQgYW5kIGZvbnRzIGluIGdncGxvdCBpcyBlYXN5LCB3aXRoIGxvdHMgb2Ygb3B0aW9ucyB0byBtYWtlIGl0IGxvb2sgZXhhY3RseSBob3cgeW91IHdhbnQgaXQuIEhlcmUncyBvdXIgZ3JhcGhpYyBmcm9tIHllc3RlcmRheS4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUoYWVzKGNvbG91cj10eXBlKSwgbGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj10eXBlKSwgc2l6ZSA9IDMsIHNoYXBlID0gOCkKYGBgCldoYXQga2luZCBvZiB0aGluZ3MgbWlnaHQgd2Ugd2FudCB0byBjaGFuZ2U/IE1heWJlIHdlIHdhbnQgYSB0aXRsZSBhbmQgbW9yZSBpbmZvcm1hdGl2ZSBhbmQgY2xlYW5lci1sb29raW5nIGF4aXMgbGFiZWxzLiBUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIGFkZCBuZXcgbGF5ZXIsIGxhYnMoKSBmb3IgbGFiZWxzLiBZb3UgY2FuIGFsc28gdXNlIHhsYWIoKSwgeWxhYigpLCBhbmQgZ2d0aXRsZSgpIHRvIGFkZCB0aGVtIGluZGl2aWR1YWxseS4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSksIGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemUgPSAzLCBzaGFwZSA9IDgpKwogICAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD0iVHJlZSBoZWlnaHQiLCB5PSJDaHJpc3RtYXMgbWFnaWMiKQpgYGAKV2hhdCBpZiB3ZSB3YW50IHRvIG1ha2UgbW9yZSBhZXN0aGV0aWMgY2hhbmdlcyB0byBvdXIgZm9udHMgYW5kIGxhYmVscz8gV2Ugd2lsbCBuZWVkIHRvIHVzZSB0aGVtZXMhCgpEQVkgMTEKCk9uIHRoZSBlbGV2ZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi53ZSBpbnRyb2R1Y2VkIHRoZW1lcyEKCldlIGNhbiB1c2UgdGhlbWVzIHRvIG1ha2UgZmluZXIgYWRqdXN0bWVudHMgdG8gbm9uLWRhdGEgcGFydHMgb2Ygb3VyIGdyYXBoaWMuIFdoaWxlIGxhYnMoKSBpcyBmaW5lIGZvciBhZGRpbmcgbGFiZWxzIGFuZCBhIHRpdGxlLCB0aGVtZXMgYWxsb3cgdXMgdG8gY2hvb3NlIHRoZSBzaXplLCBmb250LCBjb2xvdXIsIHBvc2l0aW9uLCBldGMuIG9mIHRoYXQgdGV4dC4gVG9kYXkgSSdtIGdvaW5nIHRvIGludHJvZHVjZSB5b3UgdG8gc29tZSBjb21wbGV0ZSB0aGVtZXMuCgpMZXQncyBnbyBiYWNrIHRvIG91ciBuaWNlLCBjbGVhbi1sb29raW5nIGJveHBsb3RzOgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpCmBgYApXaGlsZSB0aGUgZ3JleSBiYWNrZ3JvbnVkIGlzIHRoZSBkZWZhdWx0IGluIGdncGxvdCwgaXQncyBjZXJ0YWlubHkgbm90IGEgcmVxdWlyZW1lbnQuIFRoYXQncyBvZnRlbiBvbmUgb2YgdGhlIGZpcnN0IHRoaW5ncyBJIGNoYW5nZSEKCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfYncoKQpgYGAKVGhlbWVfYncoKSBpcyBhIGNvbXBsZXRlIHRoZW1lLCBtZWFuaW5nIHRoYXQgaXQncyBhIHRoZW1lIHRoYXQgY2FuIGJlIGFwcGxpZWQgYXMgYSBsYXllciB0aGF0IGNoYW5nZXMgdGhlIGxvb2sgb2YgeW91ciBvdmVyYWxsIHBsb3QuIFRoZW1lX2dyZSgpIGlzIHRoZSBkZWZhdWx0LiBZb3UgY2FuIGFsc28gdHJ5IG90aGVyIGNvbXBsZXRlIHRoZW1lcywgc3VjaCBhcyB0aGVtZV9kYXJrKCksIHRoZW1lX2xpZ2h0KCksIHRoZW1lX2NsYXNzaWMoKSwgdGhlbWVfdm9pZCgpIGFuZCBtb3JlLgoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgClBlcnNvbmFsbHksIEkgbGlrZSB0aGlzIG9uZSBiZXN0IQoKCkRBWSAxMgoKT24gdGhlIHR3ZWxmdGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4uIHdlIHdvcmtlZCB3aXRoIHRoZW1lKCkgYW5kIG1vcmUgY29udHJvbCB3aXRoIG5vbi1kYXRhIHBhcnRzIG9mIG91ciBncmFwaGljcyEKCkhlcmUsIEknbSBrZWVwaW5nIHRoZW1lX2NsYXNzaWMoKSBhbmQgYnVpbGRpbmcgb24gdG9wIG9mIHRoYXQuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD1OVUxMLCB5PSJUcmVlIGhlaWdodCIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNiwgY29sb3VyID0gImdvbGQiKSwKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBzaXplID0gNCwgY29sb3VyID0gInJlZCIpLAogICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsIDEpLAogICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvdXI9ImRhcmtncmVlbiIpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKCkpCmBgYApJbiB0aGlzIGNhc2UsIEkgd2FudGVkIHRvIHNob3cgeW91IGhvdyB5b3UgY2FuIGtlZXAgdGhlIGdyaWQgbGluZXMsIHdoaWxlIGNoYW5naW5nIHRvIHRoZW1lX2NsYXNzaWMoKS4gV2UndmUgYWxzbyBjaGFuZ2VkIHRoZSBzdHlsZSBhbmQgc2l6ZSBvZiB0aGUgdGl0bGUsIG1vdmVkIHRoZSBsZWdlbmQgYW5kIGNoYW5nZWQgdGhlIGNvbG91cnMsIGNoYW5nZWQgdGhlIGNvbG91ciBvZiB0aGUgeSBheGlzIGxhYmVsLCBhbmQgY2hhbmdlZCB0aGUgc2l6ZSBvZiB0aGUgeCBheGlzIHRpY2sgbGFiZWxzLiBUaGlzIGlzIGJ5IG5vIG1lYW5zIGEgcHJldHR5IGdyYXBoaWMsIGJ1dCBob3BlZnVsbHkgaXQgZ2l2ZXMgeW91IGFuIGlkZWEgb2YgZGlmZmVyZW50IHdheXMgd2UgY2FuIGNoYW5nZSBmZWF0dXJlcyBvZiBvdXIgZ3JhcGhpYy4gVGhlcmUgYXJlIG1hbnkgbW9yZSBjaGFuZ2VzIHlvdSBjb3VsZCBtYWtlIGFuZCBsb3RzIG9mIGdyZWF0IG9ubGluZSB0dXRvcmlhbHMgdGhhdCBjb3ZlciB0aGlzIGluIG1vcmUgZGV0YWlsLgoKREFZIDEzCgpPbiB0aGUgdGhpcnRlZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgaW50cm9kdWNlZCBzY2FsZXMhCgpUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIGludHJvZHVjZSBzY2FsZXMuIFdlJ2xsIGJlIHdvcmtpbmcgd2l0aCBzY2FsZXMgZm9yIGEgZmV3IGRheXMgLSBnZXQgZXhjaXRlZCEgU2NhbGVzIGFsbG93IHVzIHRvIG92ZXJyaWRlIGRlZmF1bHRzLiBTaW1pbGFyIHRvIHRoZW1lcywgc2NhbGVzIGFsbG93IHVzIG1vcmUgY29udHJvbCBvdmVyIHdoYXQgb3VyIGdyYXBoaWMgbG9va3MgbGlrZSwgYnV0IHNjYWxlcyBmb2N1cyBvbiBjaGFuZ2luZyB0aGUgbG9vayBvZiB0aGUgZGF0YS4KCkxldCdzIHN0YXJ0IHdpdGggcG9zaXRpb24gc2NhbGVzLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIGFyZSBzY2FsZV94X2NvbnRpbnVvdXMoKSBhbmQgc2NhbGVfeV9jb250aW51b3VzKCkuIFNpbmNlIHdlIGFyZSB3b3JraW5nIHdpdGggY2F0ZWdvcmljYWwgZGF0YSByaWdodCBub3cgKHRyZWUgdHlwZXMpLCB3ZSBjb3VsZCBzd2FwIG91dCBzY2FsZV94X2NvbnRpbnVvdXMoKSBmb3Igc2NhbGVfeF9kaXNjcmV0ZSgpLiBVc2luZyB0aGVzZSwgd2UgY2FuIHNldCB0aGUgbGltaXRzIG9mIG91ciBzY2FsZXMuIFdlIGRvbid0IG5lZWQgdG8gY2hhbmdlIHRoZSBsaW1pdHMgb2Ygb3VyIGRpc2NyZXRlICh4KSBheGlzLCBidXQgbGV0J3MgY2hhbmdlIHRoZSBsaW1pdHMgb2Ygb3VyIHkgYXhpcy4KCkhlcmUncyBvdXIgYm94cGxvdCBncmFwaGljLCBidXQgSSd2ZSBjaGFuZ2VkIG91ciB5IGF4aXMgdG8gQ2hyaXN0bWFzIG1hZ2ljIGluc3RlYWQgb2YgdHJlZSBoZWlnaHQ6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKTm93IGxldCdzIHRyeSBjaGFuZ2luZyB0aGUgeS1heGlzIGxpbWl0czoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT14bWFzLm1hZ2ljKSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgbGFicyh0aXRsZT0iQ2hyaXN0bWFzIFRyZWVzIiwgeD1OVUxMLCB5PSJDaHJpc3RtYXMgbWFnaWMiKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsMTUpKQpgYGAKTm90ZTogeW91IGNhbiBhbHNvIHVzZSBsaW1zKHg9YygjLCMpLCB5PWMoIywjKSkgKHJlcGxhY2luZyB0aGUgI3Mgd2l0aCB5b3VyIGRlc2lyZWQgbGltaXRzKS4gVGhpcyBpcyBzaW1wbGVyIGFuZCBmYXN0ZXIsIGJ1dCB3ZSB3aWxsIHN0aWNrIHdpdGggc2NhbGVfeV9jb250aW51b3VzKCkgYmVjYXVzZSB3ZSB3aWxsIG1ha2UgYWRkaXRpb25hbCBhZGp1c3RtZW50cyBiZWxvdy4KCldoZW4gd2UgaW5jcmVhc2VkIG91ciB5LWF4aXMgbGltaXRzLCBpdCBjaGFuZ2VzIHRoZSBsYWJlbHMgb2Ygb3VyIHktYXhpcyB0aWNrcyB0byBpbmNsdWRlIDAuNXMuIE1heWJlIHdlJ2QgcmF0aGVyIGhhdmUgd2hvbGUgbnVtYmVycyBvciBtYXliZSBqdXN0IGZld2VyIG51bWJlcnMgYWx0b2dldGhlci4gV2UgY2FuIHNwZWNpZnkgdGhpcyB1c2luZyB0aGUgc2FtZSBzY2FsZV95X2NvbnRpbnVvdXMoKSBidXQgYWRkaW5nICJicmVha3M9Ii4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygyLDEyKSwgYnJlYWtzPWMoMiwgNywgMTIpKQpgYGAKSWYgd2UgZG9uJ3Qgd2FudCBhbnkgYnJlYWtzIHdlIGNhbiBzcGVjaWZ5IGJ5IHVzaW5nICJicmVha3M9TlVMTCIuCgpGaW5hbGx5LCB3ZSBjYW4gbW9kaWZ5IHRoZSBheGlzIHRpY2sgbGFiZWxzLiBMZXQncyBjaGFuZ2UgdGhlIG5hbWVzIG9mIG91ciB4LWF4aXMgdGljayBsYWJlbHMuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9eG1hcy5tYWdpYykpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIGxhYnModGl0bGU9IkNocmlzdG1hcyBUcmVlcyIsIHg9TlVMTCwgeT0iQ2hyaXN0bWFzIG1hZ2ljIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygyLDEyKSwgYnJlYWtzPWMoMiwgNywgMTIpKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKQpgYGAKVGhlcmUncyBtdWNoIG11Y2ggbW9yZSB0aGF0IGNhbiBiZSBkb25lIHdpdGggcG9zaXRpb24gc2NhbGVzLiBJIHN1Z2dlc3QgdGFraW5nIGEgbG9vayBhdCBDaC4gMTAgb2YgdGhpcyBib29rIGJ5IEhhZGxleSBXaWNraGFtLCBEYW5pZWxsZSBOYXZhcnJvLCBhbmQgVGhvbWFzIExpbiBQZWRlcnNlbiwgd2hpY2ggSSByZWxpZWQgb24gaGVhdmlseSBmb3IgdGhpcyBzZWN0aW9uLiBodHRwczovL2dncGxvdDItYm9vay5vcmcvc2NhbGUtcG9zaXRpb24uaHRtbCNzY2FsZS1wb3NpdGlvbgoKREFZIDE0CgpPbiB0aGUgZm91cnRlZW50aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2Ugd29ya2VkIHdpdGggY29sb3VyIHNjYWxlcyEKCldlJ3ZlIGJlZW4gc2VlaW5nIHRoZSBzYW1lIHRocmVlIGNvbG91cnMgb3ZlciBhbmQgb3ZlcjogcmVkLCBncmVlbiwgYmx1ZS4gQnV0IHdoYXQgaWYgd2Ugd2FudCB0byBzcGVjaWZ5IHRoZSBjb2xvdXJzIHdoZW4gd2UgY2hvb3NlIGNvbG91ciBvciBmaWxsIGJ5IHR5cGU/IFRoaXMgaXMgd2hlcmUgY29sb3VyIHNjYWxlcyBjb21lIGluLgoKV2UgY2FuIGNob29zZSBhIGRpZmZlcmVudCBjb2xvdXIgcGFsZXR0ZXMgYnkgaW5zdGFsbGluZyBjb2xvdXIgcGFsZXR0ZSBwYWNrYWdlcyBhbmQgbG9hZGluZyB0aGVtLgoKSGVyZSBpcyBhIGdvb2QgcmVzb3VyY2UsIFJDb2xvckJyZXdlci4KYGBge3J9Cmluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpgYGAKCkxldCdzIGNob29zZSBhIHBhbGV0dGUgZnJvbSBSQ29sb3JCcmV3ZXIuIFlvdSBjYW4gZmluZCB0aGUgbmFtZXMgYW5kIHBhbGV0dGVzIGhlcmU6IGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8zOC1yY29sb3JicmV3ZXJzLXBhbGV0dGVzLmh0bWwgb3IgeW91IGNhbiBydW4gdGhpcyBjb2RlIHRvIGRpc3BsYXkgdGhlbSBpbiBSOgpgYGB7cn0KUkNvbG9yQnJld2VyOjpkaXNwbGF5LmJyZXdlci5hbGwoKQpgYGAKSGVyZSB3ZSdsbCBjcmVhdGUgb3VyIGJveHBsb3RzIGFnYWluLCBzbyB3ZSB3aWxsIHdhbnQgdG8gY2hhbmdlIHRoZSAiZmlsbCIgcmF0aGVyIHRoYW4gdGhlICJjb2xvdXIiLCB0aGVyZWZvcmUgd2UgdXNlIHNjYWxlX2ZpbGxfYnJld2VyKCkuIFdlJ3JlIGdvaW5nIHRvIHRyeSB0aGUgRGFyazIgcGFsZXR0ZToKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iRGFyazIiKQpgYGAKCldoYXQgaWYgeW91IGxpa2UgdGhlIHBhbGV0dGUgYnV0IGRvbid0IGxpa2UgaG93IGdncGxvdCBhcHBsaWVkIHRoZSBjb2xvdXJzPyBPciBtYXliZSB5b3UgY2FuJ3QgZmluZCB0aGUgcGVyZmVjdCBwYWxldHRlIGFuZCB3YW50IHRvIGNyZWF0ZSB5b3VyIG93bj8gV2UgY2FuIG1hbnVhbGx5IGFzc2lnbiBjb2xvdXJzIHRvby4uLiBidXQgd2UnbGwgd2FpdCB1bnRpbCB0b21vcnJvdyBmb3IgdGhhdCBvbmUhCgpEQVkgMTUKCk9uIHRoZSBmaWZ0ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgbGVhcm5lZCBob3cgdG8gYXNzaWduIGNvbG91cnMgbWFudWFsbHkhCgpGb3IgdGhpcyB3ZSB1c2Ugc2NhbGVfY29sb3JfbWFudWFsKCkgYW5kL29yIHNjYWxlX2ZpbGxfbWFudWFsKCkuIEZvciB0aGUgYm94cGxvdHMsIHdlIHdpbGwgdXNlIHNjYWxlX2ZpbGxfbWFudWFsKCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJkYXJrZ3JlZW4iLCAiZmlyZWJyaWNrMiIsICJtZWRpdW1zZWFncmVlbiIpKQpgYGAKVGhlIGNvbG91cnMgd2lsbCBiZSBhc3NpZ25lZCBpbiB0aGUgb3JkZXIgd2UgZ2F2ZSB0aGVtLCBzbyB5b3UgY2FuIGFsc28gcmVwZWF0IGEgY29sb3VyIChlLmcuLCBncmVlbiwgcmVkLCBncmVlbikgYW5kIGl0IHdpbGwgYmUgYXNzaWduZWQgaW4gdGhhdCBvcmRlci4gSW5zdGVhZCBvZiBjb2xvdXIgbmFtZXMsIHlvdSBjYW4gYWxzbyB1c2UgdGhlIGNvbG9yIGNvZGVzIChlLmcuLCAjRTY5RjAwKS4KCldlJ2xsIGJlIHVzaW5nIHRoZXNlIGNvbG91cnMgYWdhaW4gYW5kIGFnYWluLCBzbyB3aHkgZG9uJ3Qgd2Ugc2F2ZSB0aGVtIGFzIGEgdmVjdG9yPwpgYGB7cn0KeG1hcyA8LWMoImRhcmtncmVlbiIsICJmaXJlYnJpY2syIiwgIm1lZGl1bXNlYWdyZWVuIikKYGBgCgoKWW91IGNhbiBhbHNvIHNwZWNpZmljIHRoZSBjb2xvdXJzIHRoYXQgd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIGxldmVsIG9mIHlvdXIgdmFyaWFibGU6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz14bWFzKQpgYGAKCkNocmlzdG1hcyBjb2xvdXJzIGFyZW4ndCBuZWNlc3NhcmlseSBjb2xvdXItYmxpbmQgZnJpZW5kbHkuIFRoZXJlIGFyZSBsb3RzIG9mIGZhbnRhc3RpYyByZXNvdXJjZXMgd2hlbiBjb25zaWRlcmluZyBjb2xvdXItYmxpbmQgZnJpZW5kbHkgcGFsZXR0ZXMgZm9yIHlvdXIgZ3JhcGhpY3MuIEhlcmUncyBvbmU6IGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8jdHlwZT1zZXF1ZW50aWFsJnNjaGVtZT1ZbEduQnUmbj05CgpEQVkgMTYKCk9uIHRoZSBzaXh0ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2Ugd29ya2VkIHdpdGggYSBkaWZmZXJlbnQgdHlwZSBvZiBwbG90IGFuZCBhZGRlZCBtdWx0aXBsZSBnZW9tcyB0byBvbmUgcGxvdCEKCldlIGhhdmVuJ3QgZXZlbiB0b3VjaGVkIHRoZSBzbGVpZ2ggZGF0YXNldCEgTGV0J3MgY3JlYXRlIGEgZ3JhcGhpYyB3aXRoIHRoYXQgc28gd2UgY2FuIHdvcmsgd2l0aCBhIGRpZmZlcmVudCB0eXBlIG9mIHBsb3QuCmBgYHtyfQpzbGVpZ2hzICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKSGVyZSB3ZSd2ZSB0b2xkIGdncGxvdCB0byBjb2xvdXIgZWFjaCBzbGVpZ2ggdHlwZSBhIGRpZmZlcmVudCBjb2xvdXIgKHNvbWUgYXJlIG5lYXJseSBpbXBvc3NpYmxlIHRvIGRpZmZlcmVudGlhdGUgYnV0IHdlIHdvbid0IHdvcnJ5IGFib3V0IHRoYXQgdG9kYXkpLgoKTWF5YmUgd2Ugd2FudCB0byBhZGQgYSB0cmVuZGxpbmUgdG8gb3VyIHBsb3QuIFdlIGNhbiBkbyB0aGlzIGJ5IGFkZGluZyBhIGdlb21fc21vb3RoKCkgbGF5ZXI6CmBgYHtyfQpzbGVpZ2hzICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMpKwogIGdlb21fc21vb3RoKCkrCiAgdGhlbWVfY2xhc3NpYygpCmBgYApBbmQgbWF5YmUgd2Ugd2FudCB0byBjdXN0b21pemUgdGhhdCBsaW5lLiBUaGUgZGVmYXVsdCBpcyBhIGJsdWUgbGluZSB3aXRoIGdyZXkgYmFja2dyb3VuZC4gV2UgY2FuIGNoYW5nZSB0aGF0IHRvIGEgYmxhY2sgbGluZSB1c2luZyAiY29sb3VyID0iIGFuZCB3ZSBjYW4gY2hhbmdlIHRoZSB0cmFuc3BhcmVuY3kgb2YgdGhlIGVycm9yIHVzaW5nICJhbHBoYSA9Ii4gV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBtZXRob2Qgb2YgaG93IHRoZSBsaW5lIGlzIGNhbGN1bGF0ZWQgdXNpbmcgIm1ldGhvZCA9IgpgYGB7cn0Kc2xlaWdocyAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zKSsKICBnZW9tX3Ntb290aChjb2xvdXI9ImJsYWNrIiwgYWxwaGE9MC4yLCBtZXRob2Q9bG0pKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKQWxwaGEgY2FuIGFsc28gYmUgaGVscGZ1bCB3aGVuIHlvdSBoYXZlIG92ZXJsYXBwaW5nIHBvaW50cy4KYGBge3J9CnNsZWlnaHMgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9MywgYWxwaGE9MC41KSsKICBnZW9tX3Ntb290aChjb2xvdXI9ImJsYWNrIiwgYWxwaGE9MC4yKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCk1vcmUgZGV0YWlsZWQgYW5kIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24gY29sb3VyIHNjYWxlcyBjYW4gYmUgZm91bmQgaGVyZTogaHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnL3NjYWxlLWNvbG91ci5odG1sCgoKREFZIDE3CgpPbiB0aGUgc2V2ZW50ZWVudGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgZWRpdGVkIG91ciBsZWdlbmQhCgpXYW50IHRvIG1vdmUgeW91ciBsZWdlbmQ/IENoYW5nZSB0aGUgc2hhcGUsIHN0eWxlLCBzaXplPyBHZXQgcmlkIG9mIGl0IGNvbXBsZXRlbHk/CgpMZXQncyBnbyBiYWNrIHRvIG91ciBib3hwbG90cy5XZSBjYW4gcmVtb3ZlIHRoZSBsZWdlbmQgdXNpbmcgInNob3cubGVnZW5kID0gRkFMU0UiLiBXZSBwdXQgdGhpcyBpbiB0aGUgZ2VvbV9ib3hwbG90KCkgc2luY2UgdGhlIGxlZ2VuZCBpcyBsaW5rZWQgdG8gdGhhdCBsYXllci4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApBbm90aGVyIG9wdGlvbiBpcyB0byB1c2UgZ3VpZGVzKCkuIEluIHRoaXMgY2FzZSB3ZSB1c2UgZmlsbD0ibm9uZSIgYnV0IGlmIHdlIHdlcmUgd29ya2luZyB3aXRoIGNvbG91ciBpbnN0ZWFkIG9mIGZpbGwsIHdlIHdvdWxkIHR5cGUgY29sb3VyPSJub25lIi4gVGhpcyB3YXkgb2YgcmVtb3ZpbmcgdGhlIGxlZ2VuZCBpcyBoYW5keSBpbiBpbnN0YW5jZXMgd2hlbiB5b3UgaGF2ZSBtdWx0aXBsZSBnZW9tcy4gV2UgY2FuIGFkZCBvbmUgbGl0dGxlIGxpbmUgb2YgY29kZSB0byByZW1vdmUgdGhlIGxlZ2VuZCwgaW5zdGVhZCBvZiB0eXBpbmcgInNob3cubGVnZW5kPUYiIGludG8gZWFjaCBnZW9tIGxheWVyLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChhZXMoZmlsbD10eXBlKSwgY29sb3VyPSJibGFjayIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9eG1hcykrCiAgZ3VpZGVzKGZpbGw9Im5vbmUiKQpgYGAKCldlIGNhbiBhbHNvIGNoYW5nZSB0aGUgdGV4dCBvZiB0aGUgbGVnZW5kLiBXaGVuIHdlIGNoYW5nZWQgdGhlIHgtYXhpcyBsYWJlbHMsIHRoZSBsZWdlbmQgZGlkbid0IGNoYW5nZSB3aXRoIGl0LiBMZXQncyBmaXggdGhhdCBub3cuIFdlIGRvIHRoaXMgYnkgYWRkaW5nIHRoZSBzYW1lICJsYWJlbHMgPSBjKC4uLiIgaW4gc2NhbGVfZmlsbF9tYW51YWwoKSBhcyB3ZWxsIGFzIHNjYWxlX3hfZGlzY3JldGUoKS4gV2h5PyBCZWNhdXNlIHNjYWxlX2ZpbGxfbWFudWFsKCkgcmVmZXJzIHRvIHRoZSBjb2xvdXJzIG9mIHlvdXIgZGF0YSwgYW5kIHRoZSBsZWdlbmQgcmVwcmVzZW50cyB0aGF0ICh0aGV5IGFyZSBkaXJlY3RseSBsaW5rZWQpLiBzY2FsZV94X2Rpc2NyZXRlKCkgaXMgZm9jdXNlZCBzb2xlbHkgb24gdGhlIHgtYXhpcy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9dHlwZSksIGNvbG91cj0iYmxhY2siKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVHJlZSBUeXBlIiwgdmFsdWVzPXhtYXMsIGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKSsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkJhbHNhbSBGaXIiLCAiSmFjayBQaW5lIiwgIkJsdWUgU3BydWNlIikpCmBgYAoKKlJFRlJFU0hFUiogV2hhdCBpZiB3ZSB3YW50IHRvIGNoYW5nZSB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZCBvciB0aGUgY29sb3VyIG9mIHRoZSB0ZXh0Pz8gUmVtZW1iZXIgYmFjayB0byB3aGVuIHdlIHRhbGtlZCBhYm91dCB0aGVtZXM/IElmIHdlIHdhbnQgdG8gbWFrZSBjaGFuZ2VzIHRvIGFueXRoaW5nIHRoYXQncyBub3QgcmVsYXRlZCB0byB0aGUgZGF0YSAoaS5lLiwgaXQgY291bGQgYmUgYSBwbG90IG9mIGFueXRoaW5nIG9yIG9uZSB3aXRob3V0IGFueSBkYXRhIGluIGl0KSwgd2UgdXNlIFRIRU1FUy4KCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXR5cGUsIHk9aGVpZ2h0KSkrCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXR5cGUpLCBjb2xvdXI9ImJsYWNrIikrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRyZWUgVHlwZSIsIHZhbHVlcz14bWFzLCBsYWJlbHM9YygiQmFsc2FtIEZpciIsICJKYWNrIFBpbmUiLCAiQmx1ZSBTcHJ1Y2UiKSkrCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJCYWxzYW0gRmlyIiwgIkphY2sgUGluZSIsICJCbHVlIFNwcnVjZSIpKSsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImdvbGRlbnJvZDMiLCBzaXplID0xNCwgZmFjZT0iYm9sZCIpLCBsZWdlbmQucG9zaXRpb249InRvcCIpCmBgYAoKREFZIDE4CgpPbiB0aGUgZWlnaHRlZW50aCBkYXkgb2YgQ2hSaXN0bWFzLi4uCgouLi4gd2UgbGVhcm5lZCBhYm91dCBndWlkZXMhCgpZZXN0ZXJkYXksIHdlIG1hZGUgc29tZSBlZGl0cyB0byBvdXIgbGVnZW5kIHVzaW5nIHNjYWxlcyBhbmQgdGhlbWVzLiBUb2RheSwgd2Ugd2lsbCBpbnRyb2R1Y2Ugb25lIG1vcmUgd2F5IHRvIGV4ZXJjaXNlIG1vcmUgZmluZSBjb250cm9sIG92ZXIgeW91ciBncmFwaGljczogZ3VpZGVzKCkgYW5kIGd1aWRlXyBmdW5jdGlvbnMhIEd1aWRlcywgbGlrZSBvdXIgbGVnZW5kcyBhbmQgYXhlcywgaGVscCB1cyBvciBvdXIgYXVkaWVuY2UgaW50ZXJwcmV0IG91ciBwbG90cy4gV2UgY2FuIHVzZSBndWlkZXMoKSBvciB0aGUgZ3VpZGVfIGFyZ3VtZW50IF8qKCkgZnVuY3Rpb25zIHRvIG1ha2UgYWRkaXRpb25hbCBjaGFuZ2VzIHRvIG91ciBsZWdlbmRzIGFuZCBheGVzLiBIZXJlJ3MgYSBncmVhdCBleHBsYW5hdGlvbiBvZiBzY2FsZXMgYW5kIGd1aWRlcyBmcm9tIENoLjE1IG9mIFdpY2toYW0sIE5hdmFycm8gYW5kIFBlZGVyc29uJ3MgYm9vaywgd2hpY2ggSSBoaWdobHkgcmVjb21tZW5kIHlvdSBjaGVjayBvdXQ6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9pbmRleC5odG1sCgoiRm9ybWFsbHksIGVhY2ggc2NhbGUgaXMgYSBmdW5jdGlvbiBmcm9tIGEgcmVnaW9uIGluIGRhdGEgc3BhY2UgKHRoZSBkb21haW4gb2YgdGhlIHNjYWxlKSB0byBhIHJlZ2lvbiBpbiBhZXN0aGV0aWMgc3BhY2UgKHRoZSByYW5nZSBvZiB0aGUgc2NhbGUpLiBUaGUgYXhpcyBvciBsZWdlbmQgaXMgdGhlIGludmVyc2UgZnVuY3Rpb24sIGtub3duIGFzIHRoZSBndWlkZTogaXQgYWxsb3dzIHlvdSB0byBjb252ZXJ0IHZpc3VhbCBwcm9wZXJ0aWVzIGJhY2sgdG8gZGF0YS4gWW91IG1pZ2h0IGZpbmQgaXQgc3VycHJpc2luZyB0aGF0IGF4ZXMgYW5kIGxlZ2VuZHMgYXJlIHRoZSBzYW1lIHR5cGUgb2YgdGhpbmcsIGJ1dCB3aGlsZSB0aGV5IGxvb2sgdmVyeSBkaWZmZXJlbnQgdGhleSBoYXZlIHRoZSBzYW1lIHB1cnBvc2U6IHRvIGFsbG93IHlvdSB0byByZWFkIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBwbG90IGFuZCBtYXAgdGhlbSBiYWNrIHRvIHRoZWlyIG9yaWdpbmFsIHZhbHVlcy4iCgpMZXQncyBsb29rIGF0IHNvbWUgZXhhbXBsZXMuIExldCdzIHRyeSBhIG5ldyBncmFwaGljIHdpdGggb3VyIGRhdGEsIGFuZCB3ZSdsbCB1c2UgYSBncmFkaWVudCBjb2xvdXIgc2NhbGUgdG8gY29sb3VyIG91ciBwb2ludHMgYmFzZWQgb24gdGhlIGFtb3VudCBvZiAiQ2hyaXN0bWFzIG1hZ2ljIiBpbiBvdXIgdHJlZXM6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIpCmBgYAoKQmVmb3JlIHdlIGdldCBiYWNrIHRvIGd1aWRlcywgbGV0J3MgcXVpa2NseSBjaGF0IGFib3V0IHRoZSBncmFkaWVudCBzY2FsZS4gVGhlcmUgYXJlIG1hbnksIG1hbnkgd2F5cyB5b3UgY2FuIGVkaXQgdGhlIGNvbG91cnMsIGJ1dCBpbiB0aGlzIGNhc2Ugd2UgdG9sZCBnZ3Bsb3QgdGhhdCB3ZSB3YW50ZWQgdG8gY2hhbmdlIHRoZSBjb2xvdXIgb2Ygb3VyIHBvaW50cyB3aXRoIGEgZ3JhZGllbnQgInNjYWxlX2NvbG91cl9jb250aW51b3VzKCkiIGFuZCB0aGVuIHdlIHNldCB0aGUgaGlnaCBhbmQgbG93IGNvbG91cnMuIFdlIGNvdWxkIGhhdmUgYWxzbyBzZXQgdGhlIG1pZGRsZSBjb2xvdXIgb3IgY2hvc2VuIGFuIGV4aXN0aW5nIGdyYWRpZW50LiBMZWFybiBtb3JlIGhlcmU6IGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9zY2FsZS1jb2xvdXIuaHRtbAoKQmFjayB0byBndWlkZXMhIChjb2xvdXJzIGFyZSBqdXN0IHNvIGRpc3RyYWN0aW5nISEpCgpXZSBjYW4gbWFrZSBhZGRpdGlvbmFsIGVkaXRzIHRvIG91ciBsZWdlbmRzIHVzaW5nICIrIGd1aWRlcygpIiBvciBieSBzcGVjaWZ5aW5nIHRoZSAiZ3VpZGUgPSAiIGFyZ3VtZW50IHdpdGhpbiBvdXIgc2NhbGUgbGF5ZXIgKHNjYWxlX2NvbG91cl9jb250aW51b3VzKCksIHdoaWNoIGNvcnJlc3BvbmRzIHdpdGggb3VyIGxlZ2VuZCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIpKwogIGd1aWRlcyhjb2xvdXI9Z3VpZGVfY29sb3VyYmFyKHJldmVyc2U9VFJVRSwgZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBiYXJoZWlnaHQ9dW5pdCgyLCAiY20iKSkpCmBgYApIZXJlIHdlJ3ZlIGZsaXBwZWQgb3VyIGJhciBob3Jpem9udGFsbHkgYW5kIGluY3JlYXNlZCB0aGUgc2l6ZSBvZiB0aGUgbGVnZW5kLiBObyBjaGFuZ2VzIGhhdmUgYmVlbiBtYWRlIHRvIHRoZSByZXN0IG9mIG91ciBncmFwaGljLiBXZSBjYW4gYWNoaWV2ZSB0aGUgZXhhY3Qgc2FtZSBvdXRwdXQgYnkgYWRkaW5nICJndWlkZSA9ICIgdG8gb3VyIHNjYWxlX2NvbG91cl9jb250aW51b3VzKCkgbGF5ZXIuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PW5lZWRsZS5kcm9wLCB5PWhlaWdodCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj14bWFzLm1hZ2ljKSwgc2l6ZT0yKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX2NvbnRpbnVvdXMobG93PSJyZWQiLCBoaWdoPSJtZWRpdW1zZWFncmVlbiIsIGd1aWRlID0gZ3VpZGVfY29sb3VyYmFyKHJldmVyc2U9VFJVRSwgZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBiYXJoZWlnaHQ9dW5pdCgyLCAiY20iKSkpCmBgYApIZXJlIGFyZSBhIGNvdXBsZSBtb3JlIHdheXMgd2UgY2FuIHVzZSBndWlkZXMgdG8gZWRpdCBvdXIgbGVnZW5kLiBMZXQncyBjaGFuZ2UgdXAgb3VyIHBsb3QgYSBiaXQuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PXhtYXMubWFnaWMsIHk9bmVlZGxlLmRyb3ApKSsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksIHNpemU9Mi41KSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9eG1hcykKYGBgClRoZXJlJ3MgYSBiaXQgb2Ygb3ZlcmxhcCBpbiBvdXIgcG9pbnRzLCBzbyBsZXQncyBhZGp1c3QgdGhlIHRyYW5zcGFyZW5jeSB1c2luZyBhbHBoYToKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9eG1hcy5tYWdpYywgeT1uZWVkbGUuZHJvcCkpKwogIGdlb21fcG9pbnQoYWVzKGNvbG91cj10eXBlKSwgc2l6ZT0yLjUsIGFscGhhPTAuNykrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApCdXQgbWF5YmUgd2UgZG9uJ3Qgd2FudCBvdXIgbGVnZW5kIHRvIGFsc28gaGF2ZSB0cmFuc3BhcmVudCBwb2ludHMsIHNvIHdlIGNhbiB1c2UgYSBndWlkZSB0byBvdmVycmlkZSB0aGlzIGFlc3RoZXRpYyBjaGFuZ2UuCgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD14bWFzLm1hZ2ljLCB5PW5lZWRsZS5kcm9wKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLCBzaXplPTIuNSwgYWxwaGE9MC43KSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9eG1hcykrCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3QoYWxwaGE9MSkpKQpgYGAKRmluYWxseSwgbGV0J3MgdXNlIGd1aWRlcyB0byBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2Ygb3VyIGF4ZXMuIFdlIHdpbGwgZ28gYmFjayB0byBvdXIgc2xlaWdocyBkYXRhc2V0IGZvciB0aGlzIG9uZS4KYGBge3J9CnNsZWlnaHMgJT4lCmdncGxvdChhZXMoeD1uYW1lLCB5PWRlZXJwb3dlcikpKwogIGdlb21fcG9pbnQoKSsKICBsYWJzKHg9IlNsZWlnaHMiKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCkFzIHlvdSBjYW4gc2VlLCB0aGUgbmFtZXMgb2YgdGhlIHNsZWlnaHMgYXJlIGltcG9zc2libGUgdG8gcmVhZC4gTGV0J3MgZmxpcCB0aGUgbGFiZWxzIGF0IHRoZSBib3R0b20gc28gdGhhdCB0aGV5IHJ1biB2ZXJ0aWNhbGx5IGluc3RlYWQuCmBgYHtyfQpzbGVpZ2hzICU+JQpnZ3Bsb3QoYWVzKHg9bmFtZSwgeT1kZWVycG93ZXIpKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfY2xhc3NpYygpKwogIGxhYnMoeD0iU2xlaWdocyIpKwogIGd1aWRlcyh4PWd1aWRlX2F4aXMoYW5nbGU9OTApKQpgYGAKTXVjaCBiZXR0ZXIhIEhvcGVmdWxseSBub3cgeW91IGhhdmUgYW4gaWRlYSBvZiBzb21lIG9mIHRoZSB3YXlzIHlvdSBjYW4gZWRpdCB5b3VyIGd1aWRlcyAobGVnZW5kcywgYXhlcykgdXNpbmcgZ3VpZGVzKCkgYW5kIGd1aWRlID0uCgpEYXkgMTkKCk9uIHRoZSBuaW5ldGVlbnRoIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLiB3ZSBtYWRlIHBvc2l0aW9uIGFkanVzdG1lbnRzIQoKUG9zaXRpb24gYWRqdXN0bWVudHMgYXJlIGhhbmR5IGlmIHlvdSBoYXZlIG92ZXJsYXBwaW5nIGdlb21zIG9yIGRhdGEuIFlvdSBjYW4gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgdXNpbmcgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IGluIHRoZSBnZW9tXygpIGZ1bmN0aWlvbnMuCgpJbnN0ZWFkIG9mIGJveHBsb3RzLCBsZXQncyBsb29rIGF0IHRoZSByYXcgZGF0YSBwb2ludHMgdXNpbmcgYSBkaWZmZXJlbnQgdHlwZSBvZiBnZW9tIGxhdGVyLCBnZW9tX2ppdHRlcigpLgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXI9dHlwZSkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz14bWFzKQoKYGBgClBvc2l0aW9uIGFkanVzdG1lbnRzIGNvbWUgaW4gaGFuZHkgd2l0aCBwb2ludCBkYXRhIGxpa2UgdGhpcywgbW9yZSBzbyB3aGVuIHdlJ3JlIHdvcmtpbmcgd2l0aCBsYXJnZSBkYXRhc2V0cyB0aGF0IGhhdmUgbWFueSBwb2ludHMuIExldCdzIGFkanVzdCB0aGUgcG9zaXRpb24gb2Ygb3VyIGppdHRlcmVkIHBvaW50cy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9dHlwZSwgeT1oZWlnaHQpKSsKICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXR5cGUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAuNSkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz14bWFzKQpgYGAKTGV0J3MgbG9vayBhdCBhbm90aGVyIGV4YW1wbGUuIFdlJ2xsIGJyaW5nIGJhY2sgb3VyIHNjYXR0ZXJwbG90IG9mIHNsZWlnaCBkYXRhIGJ1dCBJJ20gZ29pbmcgdG8gY3V0IGl0IGRvd24gYSBiaXQgdG8gbWFrZSBpdCBhIGVhc2llciB0byB3b3JrIHdpdGguIEknbGwgZG8gdGhpcyB1c2luZyBhbm90aGVyIHRpZHl2ZXJzZSBwYWNrYWdlLCBkcGx5ci4KCmBgYHtyfQpzbGVpZ2hzLnRyYW5zcG9zZWQgPC10KHNsZWlnaHMpCnNsZWlnaHMuc3Vic2V0IDwtc2xpY2Uoc2xlaWdocywgNDoxMikKYGBgCgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9MykrCiAgdGhlbWVfY2xhc3NpYygpCmBgYApUaGF0IGxlZ2VuZCBpcyBmaW5lLCBidXQgbGV0J3MgZ2V0IHJpZCBvZiBpdCBhbmQgaW5zdGVhZCBsYWJlbCBlYWNoIHBvaW50LkRvIHlvdSByZW1lbWJlciBob3cgdG8gcmVtb3ZlIHRoZSBsZWdlbmQ/CmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSkpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKV2VsbCB0aGlzIG1pZ2h0IHdvcmsgYmV0dGVyLCBidXQgdGhlIGxhYmVscyBhcmUgYWxsIG92ZXJsYXBwaW5nIGFuZCBkaWZmaWN1bHQgdG8gcmVhZC4gVGhpcyBpcyB3aGVyZSBwb3NpdGlvbl9udWRnZSgpIGNvbWVzIGluIGhhbmR5IQoKYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uYW1lKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4PTEwLCB5PTAuNCkpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKQW5kIGJlY2F1c2Ugb3VyIFN0ZWFsdGggU2xlaWdoIGlzIG9mZiB0aGUgcGxvdCwgbGV0J3MgZml4IHRoYXQgdXNpbmcgd2hhdCB3ZSBsZWFybmVkIG9uIERheSAxMyBhYm91dCBsaW1pdHMuCmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSksIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeD0xMCwgeT0wLjQpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMzAwKSkKYGBgCkRBWSAyMAoKT24gdGhlIHR3ZW50aWV0aCBkYXkgb2YgQ2hyaXN0bWFzLi4uCgouLi4gd2UgZGlkIHNvbWUgcG9zaXRpb24gYWRqdXN0bWVudHMgd2l0aCBiYXIgcGxvdHMhCgpGaXJzdCwgbGV0J3MgY3JlYXRlIGEgYmFycGxvdCBzaW5jZSB3ZSBoYXZlbid0IGRvbmUgdGhhdCB5ZXQuIFdlJ2xsIGJhc2UgaXQgb24gb3VyIHByZXZpb3VzIHNsZWlnaHMgc3Vic2V0LgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zKSkrCiAgZ2VvbV9jb2woKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgClRoZSBzbGVpZ2hzIGNvbWUgd2l0aCA0LCA2LCBvciA4IGJlbGxzLiBTbyBoZXJlIHdlJ3JlIGRpc3BsYXlpbmcgdGhlIGNvdW50cyBvZiBzbGVpZ2hzIGluIGVhY2ggY2F0ZWdvcnkgKCMgb2YgYmVsbHMpLiBCdXQgbWF5YmUgd2Ugd2FudCB0byBzZWUgc29tZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGluIG91ciBiYXJwbG90LCBzdWNoIGFzIHRoZSBudW1iZXIgb2YgcmVpbnMgb24gdGhlIHNsZWlnaC4gSSd2ZSBoZWFyZCB0aGF0IFNhbnRhIHRha2VzIHRoZXNlIHRoaW5ncyBzdXBlciBzZXJpb3VzbHksIHNvIHRoaXMgaXMgY29tcGxldGVseSBwcmFjdGljYWwgYW5kIHJlYXNvbmFibGUgcGxvdC4gTm90ZSB0aGF0IHdlIGhhdmUgdG8gc3BlY2lmeSB0aGF0IHJlaW5zIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUsIG5vdCBhIGNvbnRpbnVvdXMgb25lLCB1c2luZyBhcy5jaGFyYWN0ZXIoKS4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4ndCBoYXZlIDMuNSByZWlucy4KYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1iZWxscywgeT1yZWlucywgZmlsbD1hcy5jaGFyYWN0ZXIocmVpbnMpKSkrCiAgZ2VvbV9jb2woKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApUaGUgZGVmYXVsdCBpcyBhIHN0YWNrZWQgYmFycGxvdCAocG9zaXRpb24gPSAic3RhY2siKSwgYnV0IHRoZXJlIGFyZSBvdGhlciB3YXlzIHdlIGNvdWxkIGRpc3BsYXkgdGhpcyB1c2luZyBwb3NpdGlvbiBhZGp1c3RtZW50cy4gVGhpcyBvcHRpb24gc2hvd3MgaXQgYXMgYSBwZXJjZW50IHVzaW5nIHBvc2l0aW9uID0gImZpbGwiLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zLCBmaWxsPWFzLmNoYXJhY3RlcihyZWlucykpKSsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPWMoNCwgNiwgOCkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9eG1hcykKYGBgCldlIGNhbiBhbHNvIHBvc2l0aW9uIHRoZW0gc2lkZSBieSBzaWRlIHVzaW5nIHBvc2l0aW9uID0gImRvZGdlIi4gTm90ZSB0aGF0IHRoZSByZWQgYmFyIG9uIHRoZSBsZWZ0IGFuZCB0aGUgZ3JlZW4gYmFyIG9uIHRoZSByaWdodCBhcmUgdHdvIGJhcnMgc2lkZSBieSBzaWRlLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWJlbGxzLCB5PXJlaW5zLCBmaWxsPWFzLmNoYXJhY3RlcihyZWlucykpKSsKICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDQsIDYsIDgpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXhtYXMpCmBgYApXYW50IGEgcmV2aWV3PyBUcnkgY2hhbmdpbmcgdGhlIG5hbWUgb2YgeW91ciBsZWdlbmQuCgpEQVkgMjEKCk9uIHRoZSB0d2VudHktZmlyc3QgZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgbGVhcm5lZCBhYm91dCBmYWNldGluZyEKCkZhY2V0aW5nIHByb2R1Y2VzIHNtYWxsZXIgZ3JhcGhzIHRoYXQgY2FuIGJlIGRpc3BsYXllZCBhbG9uZ3NpZGUgb25lIGFub3RoZXIuIFdlIHVzZSBmYWNldF93cmFwKCkgYW5kIGZhY2V0X2dyaWQoKSBmb3IgdGhpcy4gCgpMZXQncyBzdGFydCB3aXRoIGZhY2V0X3dyYXAoKS4gUmVtZW1iZXIgb3VyIGxpbmUgZ3JhcGggdGhhdCBsb29rcyBhIGJpdCBsaWtlIENocmlzdG1hcyBsaWdodHM/IExldCdzIHVzZSB0aGF0LiBIZXJlIGl0IGlzLCBhcyBhIHJlbWluZGVyOgpgYGB7cn0KdHJlZXMgJT4lCmdncGxvdChhZXMoeD1oZWlnaHQsIHk9eG1hcy5tYWdpYywgZ3JvdXA9dHlwZSkpKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpLCBsaW5ldHlwZT0iZGFzaGVkIikrCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLCBzaXplID0gMywgc2hhcGUgPSA4KQpgYGAKTm93LCBpbnN0ZWFkIG9mIGhhdmluZyBhbGwgdGhyZWUgbGluZXMgb24gb25lIHBsb3QsIGxldCdzIGNyZWF0ZSB0aHJlZSBzbWFsbGVyIHBsb3RzIGFuZCBkaXNwbGF5IHRoZW0gdG9nZXRoZXIuIAoKYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKICBnZW9tX2xpbmUobGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHNoYXBlID0gOCkrCiAgZmFjZXRfd3JhcCh+dHlwZSwgbmNvbD0zKQpgYGAKTGV0J3MgZG8gdGhlIHNhbWUgdGhpbmcgYnV0IHVzaW5nIGZhY2V0X2dyaWQoKS4gVGhlIHN5bnRheCBpcyBhIGxpdHRsZSBkaWZmZXJlbnQsIGJ1dCB3ZSd2ZSBwcm9kdWNlZCB0aGUgZXhhY3Qgc2FtZSBzZXQgb2YgcGxvdHMuIEluIG91ciBjYXNlLCAiLn50eXBlIiBwdXRzIHRoZSBwbG90cyBzaWRlIGJ5IHNpZGUuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiBnZW9tX2xpbmUobGluZXR5cGU9ImRhc2hlZCIpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHNoYXBlID0gOCkrCiAgZmFjZXRfZ3JpZCgufnR5cGUpCmBgYApJZiB3ZSB3YW50IHRvIHN0YWNrIG91ciBwbG90cyBpbnN0ZWFkLCB3ZSBjaGFuZ2UgdXAgdGhlIGNvZGluZyB3aXRoaW4gZmFjZXRfZ3JpZCgpLiBJbiBvdXIgY2FzZSwgInR5cGV+LiIgc3RhY2tzIHRoZSBwbG90cy4KYGBge3J9CnRyZWVzICU+JQpnZ3Bsb3QoYWVzKHg9aGVpZ2h0LCB5PXhtYXMubWFnaWMsIGdyb3VwPXR5cGUpKSsKIGdlb21fbGluZShsaW5ldHlwZT0iZGFzaGVkIikrCiAgZ2VvbV9wb2ludChzaXplID0gMywgc2hhcGUgPSA4KSsKICBmYWNldF9ncmlkKHR5cGV+LikKYGBgCk91ciBkYXRhc2V0cyBhcmVuJ3QgcmVhbGx5IHNldCB1cCBmb3IgdGhpcyB0eXBlIG9mIGdyaWQsIGJ1dCBsZXQncyBsb29rIGF0IHBsb3RzIG9mIHJlaW5zIGJ5IGJlbGxzIHRvIHNob3cgeW91IGhvdyB5b3UgY291bGQgc2V0IHVwIGZhY2V0X2dyaWQoKSB3aXRoIG11bHRpcGxlIHBsb3RzLiBBIHBsb3QgYXJlYSBpcyBwcm9kdWNlZCB3aXRoIHR3byBsZXZlbHMgZm9yIHJlaW5zIGFuZCB0aHJlZSBsZXZlbHMgZm9yIGJlbGxzLgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQlPiUKICBnZ3Bsb3QoYWVzKHg9cmVpbnMsIHk9YmVsbHMpKSsKICBmYWNldF9ncmlkKHJlaW5zfmJlbGxzKQpgYGAKWW91IGNhbiBhbHNvIHVzZSAic2NhbGVzID0iIHRvIGFkanVzdCB0aGUgc2NhbGVzIG9mIGFsbCBvciBlYWNoIG9mIHRoZSBwbG90cy4gTGV0J3MgZ28gYmFjayB0byBvdXIgZmlyc3Qgc2V0IG9mIHBsb3RzIGZyb20gdG9kYXk6CmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDgpKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MykKYGBgClRvIGFkanVzdCB0aGUgc2NhbGVzLCB3ZSBhZGQgInNjYWxlcyA9IiB0byBmYWNldF93cmFwKCkuCmBgYHtyfQp0cmVlcyAlPiUKZ2dwbG90KGFlcyh4PWhlaWdodCwgeT14bWFzLm1hZ2ljLCBncm91cD10eXBlKSkrCiAgZ2VvbV9saW5lKGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDgpKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9Mywgc2NhbGVzID0gImZyZWVfeSIpCmBgYApCeSBjaG9vc2luZyAiZnJlZV95IiB3ZSBoYXZlIGNoYW5nZWQgdGhlIHkgc2NhbGUgZnJvbSBmaXhlZCB0byBmcmVlIGJ1dCB0aGUgeC1heGlzIHJlbWFpbmVkIHRoZSBzYW1lLiBUYWtlIGEgbG9vayBhdCBob3cgdGhlIHktYXhpcyBzY2FsZSBpcyBub3cgZGlmZmVyZW50IGZvciBlYWNoIHBsb3QuIE90aGVyIG9wdGlvbnMgYXJlICJmcmVlX3giLCAiZml4ZWQiLCBvciAiZnJlZSIuCgpUaGVyZSBhcmUgbG90cyBtb3JlIHRoaW5ncyB5b3UgY2FuIGRvIHdpdGggZmFjZXRzLiBDaGVjayBvdXQgQ2guIDE3IG9mICJnZ3Bsb3QyOkVsZWdhbnQgR3JhcGhpY3MgZm9yIERhdGEgQW5hbHlzaXMiIGZvciBtb3JlOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvZmFjZXQuaHRtbAoKV2FudCBhIHJldmlldz8gVHJ5IGNoYW5naW5nIHRoZSBzdHlsZSBvZiB0aGUgcG9pbnRzIGFuZCBsaW5lcyBpbiB0aGVzZSBwbG90cy4gQWxzbywgdHJ5IHJlbW92aW5nIHRoZSBncmV5IGJhY2tncm91bmQhCgpEQVkgMjIKCk9uIHRoZSB0d2VudHktc2Vjb25kIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGFubm90YXRlZCBvdXIgcGxvdHMhCgpTb21ldGltZXMgd2UgbWF5IHdhbnQgdG8gYWRkIHRleHQgdG8gb3VyIHBsb3RzLCBub3QganVzdCB0aXRsZXMgYW5kIGxhYmVscywgYnV0IGFubm90YXRpb25zIG9uIHRoZSBkYXRhIG9yIHBsb3QgYXJlYS4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgZ2VvbV90ZXh0KCkuIFdlIGRpZCB0aGlzIG9uIERheSAxOSB3aGVuIHdlIHRhbGtlZCBhYm91dCBwb3NpdGlvbiBhZGp1c3RtZW50cy4gTm93IHdlJ3JlIGdvaW5nIHRvIGRpc2N1c3MgYW5ub3RhdGlvbnMgaW4gbW9yZSBkZXRhaWwuIExldCdzIGJyaW5nIGJhY2sgdGhhdCBncmFwaGljLCBidXQgd2l0aCBzb21lIGFkZGVkIHRleHQgYW5kIGxhYmVscy4KYGBge3J9CnNsZWlnaHMuc3Vic2V0ICU+JQogIGdncGxvdChhZXMoeD1kZWVycG93ZXIsIHk9a21fcGVyX2NhcnJvdCkpKwogIGdlb21fcG9pbnQoYWVzKGZpbGw9bmFtZSksIHNoYXBlPTIxLCBzaXplPTMsIHNob3cubGVnZW5kID0gRikrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uYW1lKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4PTEwLCB5PTAuNCkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMCwzMDApKSsKICBsYWJzKHg9IkRlZXJwb3dlciIsIHk9IktpbG9tZXRlcnMgcGVyIGNhcnJvdCIsIHRpdGxlPSJUb3Agb3B0aW9ucyBmb3IgU2FudGEncyBzbGVpZ2ggdXBncmFkZSIsIHN1YnRpdGxlPSAiU291cmNlOiBUaGUgVWx0aW1hdGUgU2xlaWdoIENhdGFsb2d1ZSAyMDIyIikKYGBgCkxldCdzIHNheSB0aGF0IHRoZSBlbGYgaW4gY2hhcmdlIHdhbnRzIHRvIHNlbmQgdGhpcyB0byBTYW50YSBidXQgd2FudHMgdG8gbWFyaywgb24gdGhlIHBsb3QsIHdoaWNoIHNsZWlnaCBpcyBoZXIgdG9wIGNob2ljZSBmb3IgU2FudGEuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBhbm5vdGF0ZSgpIGZ1bmN0aW9uLiBXZSBmaXJzdCBuZWVkIHRvIHNldCB1cCBvdXIgeCAmIHkgcmFuZ2VzIGFuZCBvdXIgY2FwdGlvbiB0ZXh0LgoKYGBge3J9CnlybmcgPC0gcmFuZ2Uoc2xlaWdocy5zdWJzZXQka21fcGVyX2NhcnJvdCkKeHJuZyA8LSByYW5nZShzbGVpZ2hzLnN1YnNldCRkZWVycG93ZXIpCmNhcHRpb24gPC0gcGFzdGUoc3Ryd3JhcCgiU3ByaW5rbGUncyB0b3AgY2hvaWNlOiBXaW50ZXIgRXhwcmVzcyIpKQpgYGAKClRoZW4gd2UgY2FuIG1ha2Ugb3VyIHBsb3Q6CmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICBnZW9tX3BvaW50KGFlcyhmaWxsPW5hbWUpLCBzaGFwZT0yMSwgc2l6ZT0zLCBzaG93LmxlZ2VuZCA9IEYpKwogIGdlb21fdGV4dChhZXMobGFiZWw9bmFtZSksIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeD0xMCwgeT0wLjQpKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMzAwKSkrCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IHhybmdbMV0sIHkgPSB5cm5nWzJdLCAKICAgIGxhYmVsID0gY2FwdGlvbiwgaGp1c3QgPSAtMC40LCB2anVzdCA9IDEsIHNpemUgPSA1LCBmb250ZmFjZT0iYm9sZCIpCmBgYAoKT3IgbWF5IHdlIHdhbnQgdG8gYW5ub3RhdGUgdGhlIHBvaW50cyBkaXJlY3RseSB0byBzaG93IFNhbnRhIHdoaWNoIG9uZXMgU3ByaW5rbGUgY2hvc2UgYXMgaGVyIHRvcCByZWNvbW1lbmRhdGlvbnMuCgpgYGB7cn0Kc2xlaWdocy5zdWJzZXQgJT4lCiAgZ2dwbG90KGFlcyh4PWRlZXJwb3dlciwgeT1rbV9wZXJfY2Fycm90KSkrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9Mywgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPW5hbWUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHg9MTAsIHk9MC40KSkrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDMwMCkpKwogIGFubm90YXRlKCJ0ZXh0IiwgeD1jKDU1KSwgeT1jKDIxLjYpLCBsYWJlbD1jKCJUb3AgY2hvaWNlISIpLCBzaXplID0gNywgZm9udGZhY2U9ImJvbGQiKQpgYGAKCkhvcGVmdWxseSBpdCdzIGNsZWFyIHdoaWNoIHBvaW50IHdlJ3JlIHJlZmVycmluZyB0byBoZXJlICgiV2ludGVyIEV4cHJlc3MiKSBidXQgaW4gY2FzZSBpdCdzIG5vdCwgd2UgY2FuIGFsc28gaGlnaGxpZ2h0IGl0IGJ5IGFkZGluZyBhbm90aGVyIGdlb21fcG9pbnQoKSBsYXllci4gTm90ZSB0aGF0IHdlIGFkZCBhZGRpdGlvbmFsIGdlb21fcG9pbnQoKSBsYXllcnMgYnV0IHRoZXkgbXVzdCBnbyBiZWZvcmUgb3VyIG9yaWdpbmFsIGdlb21fcG9pbnQgbGF5ZXIgb3IgdGhlIG9yYW5nZSBkb3RzIHdpbGwgYXBwZWFyIG9uIHRvcCBvZiB0aGUgY29sb3VyZWQgcG9pbnRzICh3aGljaCBpbiB0aGlzIGNhc2Ugd291bGQgYWxzbyBiZSBmaW5lISkuCmBgYHtyfQpzbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGVlcnBvd2VyLCB5PWttX3Blcl9jYXJyb3QpKSsKICAgZ2VvbV9wb2ludChkYXRhPWZpbHRlcihzbGVpZ2hzLnN1YnNldCwgbmFtZSA9PSJXaW50ZXIgRXhwcmVzcyIpLCBjb2xvdXI9Im9yYW5nZSIsIHNpemU9NikrCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1uYW1lKSwgc2hhcGU9MjEsIHNpemU9Mywgc2hvdy5sZWdlbmQgPSBGKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPW5hbWUpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHg9MTAsIHk9MC40KSkrCiAgdGhlbWVfY2xhc3NpYygpKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLDMwMCkpKwphbm5vdGF0ZSgidGV4dCIsIHg9Yyg1NSksIHk9YygyMS42KSwgbGFiZWw9YygiVG9wIGNob2ljZSEiKSwgc2l6ZSA9IDcsIGZvbnRmYWNlPSJib2xkIikKYGBgClRoZXJlJ3MgbXVjaCBtb3JlIHlvdSBjYW4gZG8gd2l0aCBhbm5vdGF0aW9ucy4gQXMgdXN1YWwsIEknbGwgZGlyZWN0IHlvdSB0byBnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzOiBodHRwczovL2dncGxvdDItYm9vay5vcmcvYW5ub3RhdGlvbnMuaHRtbCNkaXJlY3QtbGFiZWxsaW5nCgpXYW50IGEgcmV2aWV3PyBUcnkgY2hhbmdpbmcgdGhlIGNvbG91ciBwYWxldHRlIG9mIHRoZSBwb2ludHMgaW4gdGhpcyBwbG90LgoKREFZIDIzCgpPbiB0aGUgdHdlbnR5LXRoaXJkIGRheSBvZiBDaHJpc3RtYXMuLi4KCi4uLndlIGludHJvZHVjZWQgY293cGxvdCEhCgoiQ293cGxvdD8/Pz8/Pz8/OgoKWWVzLiBDb3dwbG90LgoKQ293cGxvdCBpcyBhbiBhZGQtb24gdG8gZ2dwbG90IGFuZCBhbGxvd3MgdXMgdG8gY29tYmluZSBzZXZlcmFsIHBsb3RzIGludG8gb25lLgoKIkhvdyBpcyB0aGF0IGRpZmZlcmVudCBmcm9tIGZhY2V0aW5nPz8iCgpDb3dwbG90IGFsbG93cyB5b3UgdG8gY29tYmluZSBwbG90cyBvZiBkaWZmZXJlbnQgdHlwZXMgaW50byBvbmUgaW1hZ2UhCgpGaXJzdCwgbGV0J3MgaW5zdGFsbCBhbmQgbG9hZCB0aGUgY293cGxvdCBwYWNrYWdlLgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpCmxpYnJhcnkoY293cGxvdCkKYGBgCgpOb3cgbGV0J3MgbWFrZSBhIGZldyBncmFwaHMuCmBgYHtyfQpzbGVpZ2gucGxvdDEgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9a21fcGVyX2NhcnJvdCwgeT1iYWdfc3BhY2UpKSsKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya2dyZWVuIikrCiAgdGhlbWVfY2xhc3NpYygpCgpzbGVpZ2gucGxvdDIgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9YmVsbHMpKSsKICBnZW9tX2JhcihmaWxsPSJmaXJlYnJpY2syIikrCiAgdGhlbWVfY2xhc3NpYygpCgpzbGVpZ2gucGxvdDMgPC1zbGVpZ2hzLnN1YnNldCAlPiUKICBnZ3Bsb3QoYWVzKHg9a21fcGVyX2NhcnJvdCwgeT1kZWVycG93ZXIpKSsKICBnZW9tX3F1YW50aWxlKGNvbG91cj0iZ29sZDMiKSsKICB0aGVtZV9jbGFzc2ljKCkKCnRyZWUucGxvdDE8LXRyZWVzICU+JQogIGdncGxvdChhZXMoeD10eXBlLCB5PWhlaWdodCkpKwogIGdlb21fYm94cGxvdChmaWxsPSJzZWFncmVlbiIpKwogIHRoZW1lX2NsYXNzaWMoKQoKdHJlZS5wbG90MiA8LXRyZWVzICU+JQogIGdncGxvdChhZXMoeD14bWFzLm1hZ2ljKSkrCiAgZ2VvbV9kb3RwbG90KGZpbGw9InRvbWF0bzMiKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCk5vdyB3ZSBjYW4gdXNlIGNvd3Bsb3QgdG8gY3JlYXRlIG9uZSBpbWFnZSB3aXRoIG11bHRpcGxlIHBsb3RzLgpgYGB7cn0KcGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIikpCmBgYApMZXQncyBjb21iaW5lIGFsbCA1IHBsb3RzIGludG8gb25lIGltYWdlLgpgYGB7cn0KcGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIHNsZWlnaC5wbG90MSwgc2xlaWdoLnBsb3QyLCBzbGVpZ2gucGxvdDMsIG5yb3cgPSAyKQpgYGAKVGhpcyBsb29rcyBva2F5LCBidXQgbWF5YmUgd2Ugd2FudGVkIHRoZSB0cmVlIHBsb3RzIHRvIGJlIG9uIHRoZSB0b3AgYW5kIHRoZSBzbGVpZ2ggcGxvdHMgdG8gYmUgb24gdGhlIGJvdHRvbS4gV2UgY2FuIGRvIHRoYXQgYnV0IHNwZWNpZnlpbmcgd2hpY2ggb25lcyBnbyBpbiB0aGUgdG9wIHJvdyBhbmQgd2hpY2ggb24gdGhlIGJvdHRvbS4KCmBgYHtyfQpmaXJzdF9yb3cgPC1wbG90X2dyaWQocGxvdF9ncmlkKHRyZWUucGxvdDEsIHRyZWUucGxvdDIsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIikpKQpzZWNvbmRfcm93IDwtcGxvdF9ncmlkKHNsZWlnaC5wbG90MSwgc2xlaWdoLnBsb3QyLCBzbGVpZ2gucGxvdDMsIG5yb3c9MSwgbGFiZWxzID0gYygiQSIsICJCIiwgIkMiKSkKCnBsb3RfZ3JpZChmaXJzdF9yb3csIHNlY29uZF9yb3csIG5jb2w9MSwgbnJvdz0yKQpgYGAKV2FudCBhIHJldmlldz8gVHJ5IGNoYW5naW5nIHlvdXIgYXhpcyBsYWJlbHMgdG8gc29tZXRoaW5nIGNsZWFuZXIgYW5kIG1vcmUgaW5mb3JtYXRpdmUgKHByZXRlbmQgeW91IHdlcmUgZ29pbmcgdG8gcHVibGlzaCB0aGlzIGltYWdlIGluIGEgcGFwZXIhKS4gSGludDogeW91IHdpbGwgbmVlZCB0byBlZGl0IHRoZSBsYWJlbHMgaW4geW91ciBvcmlnaW5hbCBwbG90cywgbm90IGluIHRoZSBwbG90X2dyaWQoKS4gSWYgeW91IGNhbid0IHJlbWVtYmVyIGhvdywgY2hlY2sgb3V0IGRheSAxMC4KCkRBWSAyNAoKT24gdGhlIHR3ZW50eS1mb3VydGggZGF5IG9mIENocmlzdG1hcy4uLgoKLi4ud2UgcHV0IG91ciBrbm93bGVkZ2UgdG8gdGhlIHRlc3QhCgpUb2RheSB3ZSB3aWxsIG5vdCBsZWFybiBhbnl0aGluZyBuZXcuIEluc3RlYWQsIHdlIHdpbGwgYnJpbmcgdG9nZXRoZXIgd2hhdCB3ZSd2ZSBsZWFybmVkIHRvIGJ1aWxkIHNvbWUgcGxvdHMhCgpXZSB3aWxsIGJ1aWxkIHRocmVlIGRpZmZlcmVudCBwbG90czoKCigxKSBCdWlsZCBhIHBsb3QgdXNpbmcgdGhlIHRyZWVzIGRhdGFzZXQgdGhhdCBzaG93cyB2aW9saW4gcGxvdHMgYnkgdHJlZSB0eXBlLCBhbHNvIGNvbG91cmVkIGJ5IHRyZWUgdHlwZS4gQWRkIGEgdGl0bGUsIGluZm9ybWF0aXZlIGF4aXMgbGFiZWxzLCBhbmQgYSBsZWdlbmQgYXQgdGhlIGJvdHRvbS4KCigyKSBCdWlsZCBhIHBsb3QgdXNpbmcgdGhlIHNsZWlnaHMgZGF0YXNldCB0aGF0IHNob3dzIGRlZXJwb3dlciBieSB3ZWlnaHQsIHdpdGggYSB0cmVuZGxpbmUsIHBvaW50cyBjb2xvdXJlZCBieSBrbSBwZXIgY2Fycm90ICh3aXRoIGEgc2l6ZSBhbmQgc2hhcGUgd2hlcmUgeW91IGNhbiBzZWUgdGhlIGNvbG91ciBkaWZmZXJlbmNlcyksIGFuZCBhbiBpbmZvcm1hdGl2ZSB0aXRsZSwgYXhpcyBsYWJlbHMsIGFuZCBsZWdlbmQuCgooMykgQnVpbGQgYW4gaW1hZ2UgdGhhdCBzaG93cyB0aGUgcHJldmlvdXMgdHdvIGdyYXBocyBzaWRlLWJ5LXNpZGUgaW4gb25lIGltYWdlIHdpdGggbGFiZWxzIEEgYW5kIEIuCgpBbmQgYXMgYSBsaXR0bGUgQ2hyaXN0bWFzIEV2ZSBnaWZ0LCBoZXJlIGlzIGEgZmFudGFzdGljIGNoZWF0c2hlZXQgZm9yIGdncGxvdDIuIEkga2VlcCBpdCBpbiBteSBib29rbWFya3MgYmFyIDopCmh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZiAtIHRoaXMgbWlnaHQgY29tZSBpbiBoYW5keSB3aGlsZSB5b3UgYXJlIHdvcmtpbmcgdGhyb3VnaCB0aGVzZSBleGVyY2lzZXMhCgpEQVkgMjUgLSBNRVJSWSBDSFJJU1RNQVMhISEKCkkgaG9wZSB5b3UgZW5qb3llZCB0aGlzIGFkdmVudCBjYWxlbmRhci4gU2ltaWxhcmx5IHRvIHRoZSBwcmV2aW91cyBvbmUsIGZvciBkYXkgMjUsIEkndmUgZ2l2ZW4geW91IGNvZGUgZm9yIGEgQ2hyaXN0bWFzIHZpc3VhbCBjcmVhdGVkIGJ5IHNvbWVvbmUgZWxzZSAoaW4gdGhpcyBjYXNlLCBkYXRhIHNjaWVudGlzdCwgSm9kaWUgQnVyY2hlbGwpLiBCdXQgdW5saWtlIGluIHRoZSBvcmlnaW5hbCBSIGFkdmVudCBjYWxlbmRhUiwgbm93IHlvdSBzaG91bGQgYmUgYWJsZSB0byB1bmRlcnN0YW5kIGEgbG90IG9mIHRoZSBjb21wb25lbnRzIGFuZCB0aGUgZ3JhbW1hciB1c2VkIGluIHRoaXMgY29kZS4gTG9hZCB0aGUgZGF0YSBhbmQgcnVuIHRoZSBjb2RlIHRvIHNlZSB3aGF0IGhhcHBlbnMhIFRoZSBvcmlnaW5hbCBibG9nIHBvc3QgY2FuIGJlIGZvdW5kIGhlcmU6IGh0dHBzOi8vdC1yZWRhY3R5bC5pby9ibG9nLzIwMTYvMTIvYS12ZXJ5LWdncGxvdDItY2hyaXN0bWFzLmh0bWwKCkZpcnN0LCBsb2FkIGluIHRoaXMgZGF0YXNldCwgd2hpY2ggaXMgYXZhaWxhYmxlIHRocm91Z2ggZ2l0IGh1Yi4KYGBge3J9CkNocmlzdG1hc1RyZWUgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90LXJlZGFjdHlsL0Jsb2ctcG9zdHMvbWFzdGVyL0NocmlzdG1hcyUyMHRyZWUlMjBiYXNlJTIwZGF0YS5jc3YiKQpgYGAKCmBgYHtyfQojIEdlbmVyYXRlIHRoZSAibGlnaHRzIgpEZXNpcmVkLkxpZ2h0cyA8LSA1MApUb3RhbC5MaWdodHMgPC0gc3VtKHJvdW5kKERlc2lyZWQuTGlnaHRzICogMC4zNSkgKyByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApICsgCiAgICAgICAgICAgICAgICAgICAgICByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTcpICsgcm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjEzKSArCiAgICAgICAgICAgICAgICAgICAgICByb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApICsgcm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjA1KSkKCkxpZ2h0cyA8LSBkYXRhLmZyYW1lKExpZ2h0cy5YID0gYyhyb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMzUpLCA0LCAxOCksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApLCA1LCAxNyksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTcpLCA2LCAxNiksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTMpLCA3LCAxNSksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApLCA4LCAxNCksIDApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMDUpLCAxMCwgMTIpLCAwKSkpCkxpZ2h0cyRMaWdodHMuWSA8LSBjKHJvdW5kKHJ1bmlmKHJvdW5kKERlc2lyZWQuTGlnaHRzICogMC4zNSksIDQsIDYpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMjApLCA3LCA4KSwgMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocnVuaWYocm91bmQoRGVzaXJlZC5MaWdodHMgKiAwLjE3KSwgOSwgMTApLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTMpLCAxMSwgMTIpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMTApLCAxMywgMTQpLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChydW5pZihyb3VuZChEZXNpcmVkLkxpZ2h0cyAqIDAuMDUpLCAxNSwgMTcpLCAwKSkKTGlnaHRzJExpZ2h0cy5Db2xvdXIgPC0gYyhyb3VuZChydW5pZihUb3RhbC5MaWdodHMsIDEsIDQpLCAwKSkKCiMgR2VuZXJhdGUgdGhlICJiYXVibGVzIgpCYXVibGVzIDwtIGRhdGEuZnJhbWUoQmF1YmxlLlggPSBjKDYsIDksIDE1LCAxNywgNSwgMTMsIDE2LCA3LCAxMCwgMTQsIDcsIDksIDExLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxNCwgOCwgMTQsIDksIDEyLCAxMSwgMTIsIDE0LCAxMSwgMTcsIDEwKSkKQmF1YmxlcyRCYXVibGUuWSA8LSBjKDQsIDUsIDQsIDQsIDUsIDUsIDUsIDYsIDYsIDYsIDgsIDgsIDgsIDgsIDEwLAogICAgICAgICAgICAgICAgICAgICAgMTAsIDExLCAxMSwgMTIsIDEzLCAxMCwgMTYsIDcsIDE0KQpCYXVibGVzJEJhdWJsZS5Db2xvdXIgPC0gZmFjdG9yKGMoMSwgMiwgMiwgMywgMiwgMywgMSwgMywgMSwgMSwgMSwgMiwgMSwgMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMsIDMsIDIsIDEsIDMsIDIsIDEsIDMsIDMsIDEpKQpCYXVibGVzJEJhdWJsZS5TaXplIDwtIGMoMSwgMywgMSwgMSwgMiwgMSwgMiwgMiwgMiwgMSwgMSwgMSwgMywgMywgMywKICAgICAgICAgICAgICAgICAgICAgICAgIDIsIDMsIDEsIDEsIDIsIDIsIDMsIDMsIDIpCgojIEdlbmVyYXRlIHRoZSBwbG90CmdncGxvdCgpICsgCiAgZ2VvbV90aWxlKGRhdGEgPSBDaHJpc3RtYXNUcmVlLCBhZXMoeCA9IFRyZWUuWCwgeSA9IFRyZWUuWSwgZmlsbCA9IFRyZWUuQ29sb3VyKSkgKwogIHNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IExpZ2h0cywgYWVzKHggPSBMaWdodHMuWCwgeSA9IExpZ2h0cy5ZLCBhbHBoYSA9IExpZ2h0cy5Db2xvdXIpLAogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z29sZGVucm9keWVsbG93Iiwgc2hhcGUgPSAxNikgKwogIGdlb21fcG9pbnQoZGF0YSA9IEJhdWJsZXMsIGFlcyh4ID0gQmF1YmxlLlgsIHkgPSBCYXVibGUuWSwgY29sb3VyID0gQmF1YmxlLkNvbG91ciwgc2l6ZSA9IEJhdWJsZS5TaXplKSwKICAgICAgICAgICAgIHNoYXBlID0gMTYpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImZpcmVicmljazIiLCAiZ29sZCIsICJkb2RnZXJibHVlMyIpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTIpICsKICB0aGVtZV9idygpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDIuNSwgeGVuZCA9IDQuNSwgeSA9IDEuNSwgeWVuZCA9IDEuNSksIGNvbG91ciA9ICJibHVldmlvbGV0Iiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSA1LjUsIHhlbmQgPSA4LjUsIHkgPSAxLjUsIHllbmQgPSAxLjUpLCBjb2xvdXIgPSAiZG9kZ2VyYmx1ZTMiLCBzaXplID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDEzLjUsIHhlbmQgPSAxNi41LCB5ID0gMS41LCB5ZW5kID0gMS41KSwgY29sb3VyID0gImJsdWV2aW9sZXQiLCBzaXplID0gMikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDE3LjUsIHhlbmQgPSAxOS41LCB5ID0gMS41LCB5ZW5kID0gMS41KSwgY29sb3VyID0gImRvZGdlcmJsdWUzIiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAzLjUsIHhlbmQgPSAzLjUsIHkgPSAwLjUsIHllbmQgPSAyLjUpLCBjb2xvdXIgPSAiYmx1ZXZpb2xldCIsIHNpemUgPSAyKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gNy4wLCB4ZW5kID0gNy4wLCB5ID0gMC41LCB5ZW5kID0gMi41KSwgY29sb3VyID0gImRvZGdlcmJsdWUzIiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxNS4wLCB4ZW5kID0gMTUuMCwgeSA9IDAuNSwgeWVuZCA9IDIuNSksIGNvbG91ciA9ICJibHVldmlvbGV0Iiwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxOC41LCB4ZW5kID0gMTguNSwgeSA9IDAuNSwgeWVuZCA9IDIuNSksIGNvbG91ciA9ICJkb2RnZXJibHVlMyIsIHNpemUgPSAyKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTEsIHkgPSAyMCwgbGFiZWwgPSAiTWVycnkgQ2hyaXN0bWFzISIsCiAgICAgICAgICAgc2l6ZSA9IDEyKSArCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCg==